IllegalStateException when running tests with Flyway
Hello,
Thank you a lot for your work.
I want to use the embedded PostgreSQL database along with flyway migrations, on a SpringBoot application. When I run this test :
@SpringBootTest
@ExtendWith(FlywayTestExtension.class)
@AutoConfigureEmbeddedDatabase(beanName = "libraryDataSource")
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, OptimizedFlywayTestExecutionListener.class })
@FlywayTest(flywayName = "libraryFlyway")
class InsertingBookTest {
@Autowired
private BookRepository bookRepository;
@Test
void testGetFirstBook() {
assertThat(bookRepository.findFirstByName("my book")).isNotNull();
}
}
It fails with the following exception :
java.lang.IllegalStateException: Using org.flywaydb.test.FlywayTestExecutionListener and org.flywaydb.test.junit.FlywayTestExecutionListener is forbidden, use io.zonky.test.db.flyway.OptimizedFlywayTestExecutionListener instead
at io.zonky.test.db.flyway.FlywayDatabaseExtension$FlywayDatabaseExtensionInterceptor.apply(FlywayDatabaseExtension.java:186)
at io.zonky.test.db.flyway.FlywayDatabaseExtension$FlywayDatabaseExtensionInterceptor.invoke(FlywayDatabaseExtension.java:163)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:728)
at org.flywaydb.core.Flyway$$SpringCGLIB$$0.clean(<generated>)
at org.flywaydb.test.FlywayTestExecutionListener.dbResetWithAnnotation(FlywayTestExecutionListener.java:381)
at org.flywaydb.test.FlywayTestExecutionListener.handleFlywayTestAnnotationForClass(FlywayTestExecutionListener.java:181)
at org.flywaydb.test.FlywayTestExecutionListener.beforeTestClass(FlywayTestExecutionListener.java:160)
Here is my dependencies :
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.flywaydb.flyway-test-extensions</groupId>
<artifactId>flyway-spring-test</artifactId>
<version>10.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.zonky.test</groupId>
<artifactId>embedded-postgres</artifactId>
<version>1.2.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.zonky.test</groupId>
<artifactId>embedded-database-spring-test</artifactId>
<version>2.6.0</version>
<scope>test</scope>
</dependency>
</dependencies>
For the sake of bug reproducing, you can use my repository : https://github.com/ii02735/spring-boot-flywaytest-multiple-datasources
Thank you a lot for your help.
Hi, thanks for your question and the reproducer.
The problem is in using the @ExtendWith(FlywayTestExtension.class) annotation, which activates the org.flywaydb.test.junit5.FlywayTestExtension class, which causes the exception you described.
Try using the @ExtendWith(SpringExtension.class) annotation instead. This should solve the problem.
@tomix26 Hi, thank you for your quick reply.
I did what you told me, I replaced @ExtendWith(FlywayTestExtension.class) with @ExtendWith(SpringExtension.class) :
@SpringBootTest
@ExtendWith(SpringExtension.class)
@AutoConfigureEmbeddedDatabase(beanName = "libraryDataSource")
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, OptimizedFlywayTestExecutionListener.class })
@FlywayTest(flywayName = "libraryFlyway")
class InsertingBookTest {
But It didn't work, this time with another error (my migrations are not loaded) :
2025-05-03T13:36:15.796+02:00 INFO 33746 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2025-05-03T13:36:15.796+02:00 INFO 33746 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2025-05-03T13:36:16.216+02:00 INFO 33746 --- [ main] org.ii02735.InsertingBookTest : Started InsertingBookTest in 5.037 seconds (process running for 6.208)
2025-05-03T13:36:16.219+02:00 INFO 33746 --- [ main] o.f.test.FlywayTestExecutionListener : ---> Start reset database for 'org.ii02735.InsertingBookTest'.
2025-05-03T13:36:16.234+02:00 INFO 33746 --- [ main] o.f.test.FlywayTestExecutionListener : <--- Finished reset database for 'org.ii02735.InsertingBookTest'.
2025-05-03T13:36:16.414+02:00 INFO 33746 --- [gres:pid(33837)] i.z.t.d.p.embedded.EmbeddedPostgres : 2025-05-03 13:36:16.414 CEST [33857] ERROR: relation "book" does not exist at character 31
2025-05-03T13:36:16.414+02:00 INFO 33746 --- [gres:pid(33837)] i.z.t.d.p.embedded.EmbeddedPostgres : 2025-05-03 13:36:16.414 CEST [33857] STATEMENT: select b1_0.id,b1_0.name from book b1_0 where b1_0.name=$1 fetch first $2 rows only
2025-05-03T13:36:16.419+02:00 WARN 33746 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 42P01
2025-05-03T13:36:16.419+02:00 ERROR 33746 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: relation "book" does not exist
Position : 31
org.springframework.dao.InvalidDataAccessResourceUsageException: JDBC exception executing SQL [select b1_0.id,b1_0.name from book b1_0 where b1_0.name=? fetch first ? rows only] [ERROR: relation "book" does not exist
Position : 31] [n/a]; SQL [n/a]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:281)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:256)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:241)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:560)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:343)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:160)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
And after digging a bit, The @SpringBootTest annotation already offers @ExtendWith(SpringExtension.class) :
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@ExtendWith({SpringExtension.class})
public @interface SpringBootTest {
@AliasFor("properties")
So I tried to simplify my annotations on my test class :
@SpringBootTest
@AutoConfigureEmbeddedDatabase(beanName = "libraryDataSource")
@FlywayTest(flywayName = "libraryFlyway")
class InsertingBookTest {
@Autowired
private BookRepository bookRepository;
@Test
void testGetFirstBook() {
assertThat(bookRepository.findFirstByName("my book")).isNotNull();
}
}
But I get the same error as above.
Here's a working version: https://github.com/tomix26/spring-boot-flywaytest-multiple-datasources/commit/1b56b0f7f692288c2ea8a215cd159d3891fe7036
The main issue was missing FlywayMigrationInitializer beans, which ensure Flyway migrations are processed during context/application startup. The Zonky library expects this initialization, as from a typical application's perspective, this is the desired behavior. From a testing perspective, where migrations are executed using the @FlywayTest annotation, it's theoretically possible to skip this initial initialization. However, the Zonky library doesn't account for this, which leads to inconsistency with the H2 database. I'll try to address this issue in future versions.
@tomix26 Hello, thank you very much for your detailed explanation and for your solution.
I'm going to stick with it, as it's the closest to what I need.
Thank you again for your work 🙂