Ada*_*m B 10 junit spring unit-testing hibernate
我正在使用Spring事务测试类来对我的DAO代码进行单元测试.我想要做的是在所有测试运行之前创建一次我的数据库.我有一个@BeforeClass注释方法,但是在Spring加载应用程序上下文并配置jdbcTemplate之前运行,因此我当时实际上并没有连接到DB.有没有办法在上下文加载后但在测试开始运行之前运行我的数据库设置?
这个问题同样问题,但接受的解决方案似乎只是"不要那样做".我倾向于说这似乎是不可行的.
我的解决方案,有点复杂,但我需要它用于测试框架:-)不要害怕德国javadocs,方法名称和正文应该足以得到它
FIRST创建Annotation以标记数据库工作的类或方法(创建表和/或插入语句)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SchemaImport {
/**
* Location der Schemadatei(en). Die Datei darf nur SQL Statements enthalten.
* Wird keine Location gesetzt, greift der Defaultwert.
* @return String
*/
String[] locationsBefore() default {"input/schemas/before.sql"};
/**
* Location der Schemadatei(en). Die Datei darf nur SQL Statements enthalten.
* Wird keine Location gesetzt, greift der Defaultwert.
* @return String
*/
String[] locationsAfter() default {"input/schemas/after.sql"};
/**
* Ein SchemaImport findet nur bei passender Umgebungsvariable statt, mit diesem
* Flag kann dieses Verhalten geändert werden.
* @return boolean
*/
boolean override() default false;
}
SECOND创建了一个查找注释的侦听器,AbstractTestExecutionListener是一个Spring Framework类
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.test</artifactId>
<version>2.5.6</version>
</dependency>
public class SchemaImportTestExecutionListener extends AbstractTestExecutionListener
implements ApplicationContextAware {
/**
* Standard LOG Definition.
*/
private static final Logger LOG = LoggerFactory.getLogger(
SchemaImportTestExecutionListener.class);
/**
* Datasource Name - gemeint ist der Name der Datasource Bean bzw. die ID.
*/
private static final String DATASOURCE_NAME = "dataSource";
/**
* JDBC Template.
*/
private SimpleJdbcTemplate simpleJdbcTemplate;
/**
* Flag um festzustellen ob prepareTestInstance schon gerufen wurde.
*/
private boolean isAlreadyPrepared = false;
/**
* Standard Constructor, laut API von konkreten Implementierungen für
* TestexecutionListener erwartet, es geht aber auch ohne.
*/
public SchemaImportTestExecutionListener() {
}
/**
* Für jede Testklasse die mit der {@link SchemaImport} Annotation ausgezeichnet
* ist, wird ein entsprechender SchemaImport durchgeführt.
*
* Der SchemaImport findet pro Klasse exakt einmal statt. Diese Verhalten
* entspricht der BeforeClass
* Annotation von JUnit.
*
* Achtung mit Nutzung von Schemaimport auf Klassenebene ist kein
* Rollback möglich, stattdessen SchemaImport auf Methodenebene nutzen.
*
* @param testContext
* @throws java.lang.Exception
*/
@Override
public void prepareTestInstance(TestContext testContext) throws Exception {
final SchemaImport annotation = AnnotationUtils.findAnnotation(testContext.getTestClass(), SchemaImport.class);
if ((annotation != null) && !isAlreadyPrepared && (isPropertyOrOverride(annotation))) {
executeSchemaImports(testContext, annotation.locationsBefore(), true);
isAlreadyPrepared = true;
}
}
/**
* Für jede Testmethode mit {@link SchemaImport} werden die angegebenen
* Schema Dateien als SQL ausgeführt.
*
* @param testContext
* @throws java.lang.Exception
*/
@Override
public void beforeTestMethod(TestContext testContext) throws Exception {
// nur für Methoden mit passender Annotation Schemaimport durchführen
final SchemaImport annotation = AnnotationUtils.findAnnotation(testContext.getTestMethod(), SchemaImport.class);
if (annotation != null) {
executeSchemaImports(testContext, annotation.locationsBefore(), true);
}
}
@Override
public void afterTestMethod(TestContext testContext) throws Exception {
// nur für Methoden mit passender Annotation Schemaimport durchführen
final SchemaImport annotation = AnnotationUtils.findAnnotation(testContext.getTestMethod(), SchemaImport.class);
if (annotation != null) {
executeSchemaImports(testContext, annotation.locationsAfter(), false);
}
}
/**
* Prüfen ob passende Umgebungsvariable gesetzt wurde. Diese kann durch
* entsprechendes Setzen des Flags an der Annotation überschrieben werden.
* @return
*/
private boolean isPropertyOrOverride(SchemaImport annotation) {
String prop = System.getProperty(TYPEnviroment.KEY_ENV);
if (StringUtils.trimToEmpty(prop).equals(TYPEnviroment.EMBEDDED.getEnv())) {
LOG.info("Running SchemaImport, Enviroment is set:'" + prop + "'");
return true;
} else {
if (annotation.override()) {
LOG.warn(
"Running SchemaImport, although Enviroment is set:'" + prop + "'");
return true;
} else {
LOG.warn(
"Not Running SchemaImport cause neither Environment or SchemaImport.override are set.");
return false;
}
}
}
/**
* Hilfesmethode die eigentlichen SchemaImport kapselt.
*
* @param testContext
* @param locations
*/
private void executeSchemaImports(TestContext testContext, String[] locations, boolean checkLocations) {
// für jede Datei SchemaImport durchführen, korrekte Reihenfolge
// ist durch Entwickler zu gewährleisten
if (locations.length > 0) {
for (String location : locations) {
if (StringUtils.trimToNull(location) != null) {
if (isResourceExistant(location, checkLocations)) {
LOG.info("Executing Schema Location: '" + location + "'");
SimpleJdbcTestUtils.executeSqlScript(getJdbcTemplate(
testContext), new ClassPathResource(location),
false);
} else {
LOG.warn(
"Schema Location '" + location + "' for SchemaImport not found.");
}
} else {
throw new RuntimeException("SchemaImport with empty Locations in:'" + testContext.getTestClass().getSimpleName() + "'");
}
}
}
}
/**
*
* @param resource
* @return
*/
private boolean isResourceExistant(String resource, boolean checkLocations) {
try {
new ClassPathResource(resource).getInputStream();
return true;
} catch (IOException ex) {
if (checkLocations) {
throw new RuntimeException(ex);
} else {
return false;
}
}
}
/**
* Hilfsmethode um an ein JdbcTemplate heranzukommen.
*
* @param TestContext
* @return SimpleJdbcTemplate
*/
private SimpleJdbcTemplate getJdbcTemplate(TestContext context) {
if (this.simpleJdbcTemplate == null) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(getDataSource(
context));
}
return this.simpleJdbcTemplate;
}
/**
* Hilfsmethode um an die Datasource heranzukommen.
*
* @param testContext
* @return DataSource
*/
private DataSource getDataSource(TestContext testContext) {
return (DataSource) testContext.getApplicationContext().getBean(
DATASOURCE_NAME, DataSource.class);
}
/** {@inheritDoc} */
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
throw new UnsupportedOperationException("Not supported yet.");
}
}
THIRD将监听器添加到测试执行中
@ContextConfiguration(locations = {"classpath*:spring/persistence/*.xml"})
@Transactional
@TestExecutionListeners({
TransactionalTestExecutionListener.class,
SchemaImportTestExecutionListener.class})
public abstract class AbstractAvHibernateTests extends AbstractAvTests {
/**
* SimpleJdbcTemplate für Subclasses verfügbar.
*/
@Autowired
protected SimpleJdbcTemplate simpleJdbcTemplate;
}
正在使用
@SchemaImport(locationsBefore={"schemas/spring-batch/2.0.0/schema-hsqldb.sql"})
public class FooTest extends AbstractAvHibernateTests {
}
重要的是要注意 - 在使用testNg并行测试时要小心线程问题,为此工作应该有一些'同步'标记用于侦听器中的getJdbcTemplate/dataSource方法
PS:
测试基类的代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:spring/*.xml"})
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
LogDurationTestExecutionListener.class,
LogMethodNameTestExecutionListener.class})
public abstract class AbstractAvTests implements ApplicationContextAware {
/**
* Logger für Subclasses verfügbar.
*/
protected final Logger LOG = LoggerFactory.getLogger(getClass());
/**
* {@link ApplicationContext} für Subclasses verfügbar.
*/
protected ApplicationContext applicationContext;
/** {@inheritDoc } */
@Override
public final void setApplicationContext(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
LogDurationTestExecutionListener和LogMethodNameTestExecutionListener是spring不提供的自定义侦听器,但schemaImport无法正常工作
我赞同的建议是,您应该使每个测试都是自主的,因此使用 @Before 而不是 @BeforeClass 进行所有设置。
如果您想坚持使用您的方法,只需使用 @Before 方法并进行简单的布尔检查以查看设置是否已完成。例如
if(!databaseSetup) {
...set up the database
databaseSetup=true;
}
Run Code Online (Sandbox Code Playgroud)
不太花哨,但它会起作用!
请参阅我的答案,了解使用 dbunit 进行注释的 Spring 事务测试示例。
希望这可以帮助!
归档时间: |
|
查看次数: |
10517 次 |
最近记录: |