мое решение, немного сложное, но оно мне понадобилось для тестовой среды :-)
не бойтесь немецких javadocs, имена методов и тела должны быть достаточными, чтобы получить его
FIRST создание аннотации для пометки класса или метода для работы с базой данных (создание операторов таблицы и / или вставки)
@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.
* <p>
* Der SchemaImport findet pro Klasse exakt einmal statt. Diese Verhalten
* entspricht der <a href="http://junit.sourceforge.net/javadoc/org/junit/BeforeClass.html" rel="noreferrer">BeforeClass</a>
* Annotation von JUnit.
* <p>
* <strong>Achtung mit Nutzung von Schemaimport auf Klassenebene ist kein
* Rollback möglich, stattdessen SchemaImport auf Methodenebene nutzen.</strong>
*
* @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 должны быть некоторые синхронизированные метки
пс:
код для теста базового класса:
@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