Skip to content

Commit 3b5ba4d

Browse files
committed
fix support of multiple flyway test annotations
1 parent 6235ef2 commit 3b5ba4d

File tree

5 files changed

+147
-20
lines changed

5 files changed

+147
-20
lines changed

embedded-database-spring-test-core/src/main/java/io/zonky/test/db/flyway/OptimizedFlywayTestExecutionListener.java

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import com.google.common.collect.Iterables;
2020
import com.google.common.collect.ObjectArrays;
2121
import org.apache.commons.lang3.ArrayUtils;
22-
import org.apache.commons.lang3.StringUtils;
2322
import org.flywaydb.core.Flyway;
2423
import org.flywaydb.core.api.MigrationVersion;
2524
import org.flywaydb.core.api.resolver.MigrationResolver;
@@ -32,6 +31,8 @@
3231
import org.flywaydb.test.annotation.FlywayTest;
3332
import org.flywaydb.test.annotation.FlywayTests;
3433
import org.flywaydb.test.junit.FlywayTestExecutionListener;
34+
import org.slf4j.Logger;
35+
import org.slf4j.LoggerFactory;
3536
import org.springframework.beans.BeansException;
3637
import org.springframework.context.ApplicationContext;
3738
import org.springframework.core.annotation.AnnotationUtils;
@@ -55,6 +56,8 @@
5556
*/
5657
public class OptimizedFlywayTestExecutionListener extends FlywayTestExecutionListener {
5758

59+
private static final Logger logger = LoggerFactory.getLogger(OptimizedFlywayTestExecutionListener.class);
60+
5861
/**
5962
* The order value must be less than {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener}.
6063
* Otherwise, the flyway initialization can result in non-deterministic behavior related to transaction isolation.
@@ -78,6 +81,9 @@ public void beforeTestClass(TestContext testContext) throws Exception {
7881
FlywayTests containerAnnotation = AnnotationUtils.findAnnotation(testClass, FlywayTests.class);
7982
if (containerAnnotation != null) {
8083
FlywayTest[] annotations = containerAnnotation.value();
84+
if (annotations.length > 1) {
85+
logger.warn("Optimized database loading is not supported when using multiple flyway test annotations");
86+
}
8187
for (FlywayTest annotation : annotations) {
8288
optimizedDbReset(testContext, annotation);
8389
}
@@ -94,6 +100,9 @@ public void beforeTestMethod(TestContext testContext) throws Exception {
94100
FlywayTests containerAnnotation = AnnotationUtils.findAnnotation(testMethod, FlywayTests.class);
95101
if (containerAnnotation != null) {
96102
FlywayTest[] annotations = containerAnnotation.value();
103+
if (annotations.length > 1) {
104+
logger.warn("Optimized database loading is not supported when using multiple flyway test annotations");
105+
}
97106
for (FlywayTest annotation : annotations) {
98107
optimizedDbReset(testContext, annotation);
99108
}
@@ -108,8 +117,8 @@ private void optimizedDbReset(TestContext testContext, FlywayTest annotation) th
108117

109118
ApplicationContext applicationContext = testContext.getApplicationContext();
110119

111-
FlywayDataSourceContext dataSourceContext = getDataSourceContext(applicationContext, annotation.flywayName());
112120
Flyway flywayBean = ReflectionTestUtils.invokeMethod(this, "getBean", applicationContext, Flyway.class, annotation.flywayName());
121+
FlywayDataSourceContext dataSourceContext = getDataSourceContext(applicationContext, flywayBean);
113122

114123
if (dataSourceContext != null && flywayBean != null) {
115124
prepareDataSourceContext(dataSourceContext, flywayBean, annotation);
@@ -203,16 +212,22 @@ private static PlaceholderReplacer createPlaceholderReplacer(Flyway flyway) {
203212
return PlaceholderReplacer.NO_PLACEHOLDERS;
204213
}
205214

206-
private static FlywayDataSourceContext getDataSourceContext(ApplicationContext context, String flywayName) {
215+
private static FlywayDataSourceContext getDataSourceContext(ApplicationContext context, Flyway flywayBean) {
216+
Map<String, Flyway> flywayBeans = context.getBeansOfType(Flyway.class);
217+
String flywayBeanName = flywayBeans.entrySet().stream()
218+
.filter(e -> e.getValue() == flywayBean)
219+
.map(Map.Entry::getKey)
220+
.findFirst().orElse("default");
221+
207222
try {
208-
if (StringUtils.isBlank(flywayName)) {
209-
return context.getBean(FlywayDataSourceContext.class);
210-
} else {
211-
return context.getBean(flywayName + "DataSourceContext", FlywayDataSourceContext.class);
212-
}
213-
} catch (BeansException e) {
214-
return null;
215-
}
223+
return context.getBean(flywayBeanName + "DataSourceContext", FlywayDataSourceContext.class);
224+
} catch (BeansException e) {}
225+
226+
try {
227+
return context.getBean(FlywayDataSourceContext.class);
228+
} catch (BeansException e) {}
229+
230+
return null;
216231
}
217232

218233
private static FlywayTest copyAnnotation(FlywayTest annotation, boolean invokeCleanDB, boolean invokeBaselineDB, boolean invokeMigrateDB) {

embedded-database-spring-test-core/src/main/java/io/zonky/test/db/postgres/EmbeddedPostgresContextCustomizerFactory.java

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616

1717
package io.zonky.test.db.postgres;
1818

19+
import com.google.common.collect.ImmutableMap;
1920
import io.zonky.test.db.AutoConfigureEmbeddedDatabase;
2021
import io.zonky.test.db.AutoConfigureEmbeddedDatabase.EmbeddedDatabaseType;
2122
import io.zonky.test.db.AutoConfigureEmbeddedDatabase.Replace;
2223
import io.zonky.test.db.flyway.FlywayDataSourceContext;
2324
import org.apache.commons.lang3.StringUtils;
2425
import org.flywaydb.core.Flyway;
2526
import org.flywaydb.test.annotation.FlywayTest;
27+
import org.flywaydb.test.annotation.FlywayTests;
2628
import org.slf4j.Logger;
2729
import org.slf4j.LoggerFactory;
2830
import org.springframework.beans.BeansException;
@@ -37,14 +39,17 @@
3739
import org.springframework.context.ConfigurableApplicationContext;
3840
import org.springframework.context.support.AbstractApplicationContext;
3941
import org.springframework.core.annotation.AnnotatedElementUtils;
42+
import org.springframework.core.annotation.AnnotationUtils;
4043
import org.springframework.test.context.ContextConfigurationAttributes;
4144
import org.springframework.test.context.ContextCustomizer;
4245
import org.springframework.test.context.ContextCustomizerFactory;
4346
import org.springframework.test.context.MergedContextConfiguration;
4447
import org.springframework.util.ObjectUtils;
4548

4649
import javax.sql.DataSource;
50+
import java.lang.reflect.AnnotatedElement;
4751
import java.util.List;
52+
import java.util.Map;
4853

4954
/**
5055
* Implementation of the {@link org.springframework.test.context.ContextCustomizerFactory} interface,
@@ -81,7 +86,7 @@ private PreloadableEmbeddedPostgresContextCustomizer(AutoConfigureEmbeddedDataba
8186
@Override
8287
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
8388
Class<?> testClass = mergedConfig.getTestClass();
84-
FlywayTest flywayAnnotation = AnnotatedElementUtils.findMergedAnnotation(testClass, FlywayTest.class);
89+
FlywayTests flywayAnnotations = findFlywayTestAnnotations(testClass);
8590

8691
BeanDefinitionRegistry registry = getBeanDefinitionRegistry(context);
8792
RootBeanDefinition registrarDefinition = new RootBeanDefinition();
@@ -90,7 +95,7 @@ public void customizeContext(ConfigurableApplicationContext context, MergedConte
9095
registrarDefinition.getConstructorArgumentValues()
9196
.addIndexedArgumentValue(0, databaseAnnotation);
9297
registrarDefinition.getConstructorArgumentValues()
93-
.addIndexedArgumentValue(1, flywayAnnotation);
98+
.addIndexedArgumentValue(1, flywayAnnotations);
9499

95100
registry.registerBeanDefinition("preloadableEmbeddedPostgresRegistrar", registrarDefinition);
96101
}
@@ -115,11 +120,11 @@ public int hashCode() {
115120
private static class PreloadableEmbeddedPostgresRegistrar implements BeanDefinitionRegistryPostProcessor {
116121

117122
private final AutoConfigureEmbeddedDatabase databaseAnnotation;
118-
private final FlywayTest flywayAnnotation;
123+
private final FlywayTests flywayAnnotations;
119124

120-
private PreloadableEmbeddedPostgresRegistrar(AutoConfigureEmbeddedDatabase databaseAnnotation, FlywayTest flywayAnnotation) {
125+
private PreloadableEmbeddedPostgresRegistrar(AutoConfigureEmbeddedDatabase databaseAnnotation, FlywayTests flywayAnnotations) {
121126
this.databaseAnnotation = databaseAnnotation;
122-
this.flywayAnnotation = flywayAnnotation;
127+
this.flywayAnnotations = flywayAnnotations;
123128
}
124129

125130
@Override
@@ -131,7 +136,7 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) t
131136
RootBeanDefinition dataSourceDefinition = new RootBeanDefinition();
132137
dataSourceDefinition.setPrimary(dataSourceInfo.getBeanDefinition().isPrimary());
133138

134-
BeanDefinitionHolder flywayInfo = getFlywayBeanDefinition(beanFactory, flywayAnnotation);
139+
BeanDefinitionHolder flywayInfo = getFlywayBeanDefinition(beanFactory, flywayAnnotations);
135140
if (flywayInfo == null) {
136141
dataSourceDefinition.setBeanClass(EmptyEmbeddedPostgresDataSourceFactoryBean.class);
137142
} else {
@@ -201,7 +206,13 @@ protected static BeanDefinitionHolder getDataSourceBeanDefinition(ConfigurableLi
201206
throw new IllegalStateException("No primary DataSource found, embedded version will not be used");
202207
}
203208

204-
protected static BeanDefinitionHolder getFlywayBeanDefinition(ConfigurableListableBeanFactory beanFactory, FlywayTest annotation) {
209+
protected static BeanDefinitionHolder getFlywayBeanDefinition(ConfigurableListableBeanFactory beanFactory, FlywayTests annotations) {
210+
if (annotations != null && annotations.value().length > 1) {
211+
return null; // optimized loading is not supported yet when using multiple flyway test annotations
212+
}
213+
214+
FlywayTest annotation = annotations != null && annotations.value().length == 1 ? annotations.value()[0] : null;
215+
205216
if (annotation != null && StringUtils.isNotBlank(annotation.flywayName())) {
206217
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(annotation.flywayName());
207218
return new BeanDefinitionHolder(beanDefinition, annotation.flywayName());
@@ -228,4 +239,19 @@ protected static BeanDefinitionHolder getFlywayBeanDefinition(ConfigurableListab
228239

229240
return null;
230241
}
242+
243+
private static FlywayTests findFlywayTestAnnotations(AnnotatedElement element) {
244+
FlywayTests flywayContainerAnnotation = AnnotatedElementUtils.findMergedAnnotation(element, FlywayTests.class);
245+
FlywayTest flywayAnnotation = AnnotatedElementUtils.findMergedAnnotation(element, FlywayTest.class);
246+
247+
if (flywayContainerAnnotation != null) {
248+
return flywayContainerAnnotation;
249+
}
250+
if (flywayAnnotation == null) {
251+
return null;
252+
}
253+
254+
Map<String, Object> attributes = ImmutableMap.of("value", new FlywayTest[] { flywayAnnotation });
255+
return AnnotationUtils.synthesizeAnnotation(attributes, FlywayTests.class, null);
256+
}
231257
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package io.zonky.test.db.postgres;
2+
3+
import io.zonky.test.db.AutoConfigureEmbeddedDatabase;
4+
import org.flywaydb.core.Flyway;
5+
import org.flywaydb.test.annotation.FlywayTest;
6+
import org.junit.Test;
7+
import org.junit.runner.RunWith;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.context.annotation.Bean;
10+
import org.springframework.context.annotation.Configuration;
11+
import org.springframework.jdbc.core.JdbcTemplate;
12+
import org.springframework.test.context.ContextConfiguration;
13+
import org.springframework.test.context.junit4.SpringRunner;
14+
15+
import javax.sql.DataSource;
16+
import java.util.List;
17+
import java.util.Map;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.assertj.core.api.Assertions.tuple;
21+
22+
@RunWith(SpringRunner.class)
23+
@FlywayTest(flywayName = "flyway1")
24+
@FlywayTest(flywayName = "flyway3", invokeCleanDB = false)
25+
@AutoConfigureEmbeddedDatabase(beanName = "dataSource")
26+
@ContextConfiguration
27+
public class MultipleFlywayBeansClassLevelIntegrationTest {
28+
29+
private static final String SQL_SELECT_PERSONS = "select * from test.person";
30+
31+
@Configuration
32+
static class Config {
33+
34+
@Bean
35+
public Flyway flyway1(DataSource dataSource) {
36+
Flyway flyway = new Flyway();
37+
flyway.setDataSource(dataSource);
38+
flyway.setSchemas("test");
39+
flyway.setLocations("db/migration", "db/test_migration/dependent");
40+
return flyway;
41+
}
42+
43+
@Bean
44+
public Flyway flyway2(DataSource dataSource) {
45+
Flyway flyway = new Flyway();
46+
flyway.setDataSource(dataSource);
47+
flyway.setSchemas("test");
48+
flyway.setLocations("db/test_migration/separated");
49+
return flyway;
50+
}
51+
52+
@Bean
53+
public Flyway flyway3(DataSource dataSource) {
54+
Flyway flyway = new Flyway();
55+
flyway.setDataSource(dataSource);
56+
flyway.setSchemas("test");
57+
flyway.setLocations("db/test_migration/appendable");
58+
flyway.setValidateOnMigrate(false);
59+
return flyway;
60+
}
61+
62+
@Bean
63+
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
64+
return new JdbcTemplate(dataSource);
65+
}
66+
}
67+
68+
@Autowired
69+
private DataSource dataSource;
70+
71+
@Autowired
72+
private JdbcTemplate jdbcTemplate;
73+
74+
@Test
75+
public void databaseShouldBeLoadedByFlyway1AndAppendedByFlyway3() throws Exception {
76+
assertThat(dataSource).isNotNull();
77+
78+
List<Map<String, Object>> persons = jdbcTemplate.queryForList(SQL_SELECT_PERSONS);
79+
assertThat(persons).isNotNull().hasSize(3);
80+
81+
assertThat(persons).extracting("id", "first_name", "last_name").containsExactlyInAnyOrder(
82+
tuple(1L, "Dave", "Syer"),
83+
tuple(2L, "Tom", "Hanks"),
84+
tuple(3L, "Will", "Smith"));
85+
}
86+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
@RunWith(SpringRunner.class)
4040
@AutoConfigureEmbeddedDatabase(beanName = "dataSource")
4141
@ContextConfiguration
42-
public class MultipleFlywayBeansIntegrationTest {
42+
public class MultipleFlywayBeansMethodLevelIntegrationTest {
4343

4444
private static final String SQL_SELECT_PERSONS = "select * from test.person";
4545

embedded-database-spring-test-core/src/test/resources/logback.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<logger name="org.flywaydb" level="INFO"/>
1313

14-
<root level="ERROR">
14+
<root level="WARN">
1515
<appender-ref ref="STDOUT" />
1616
</root>
1717

0 commit comments

Comments
 (0)