Affects: spring-test 6.1.4


There is a bug, when using @Sql with BEFORE_TEST_CLASS.

Without this annotation the order of execution of methods is:

  • method annotated with @BeforeAll
  • method annotated with @DynamicPropertySource
  • starting spring (often used to start some mock, then pass it to properties)

BUT when @Sql with BEFORE_TEST_CLASS is used, the order is:

  • method annotated with @DynamicPropertySource
  • starting spring
  • method annotated with @BeforeAll

I have created sample repo: https://github.com/marwin1991/sql-before-class-bug-reproduction

with tests:

  • https://github.com/marwin1991/sql-before-class-bug-reproduction/blob/main/src/test/java/com/github/marwin1991/sqlbeforeclassbugreproduction/WithoutSqlAnnotationTest.java
  • https://github.com/marwin1991/sql-before-class-bug-reproduction/blob/main/src/test/java/com/github/marwin1991/sqlbeforeclassbugreproduction/WithSqlAnnotationTest.java

Second test not passing, because the order IMO is incorrect: it should not change after adding @Sql.

Comment From: sbrannen

Hi @marwin1991,

Congratulations on submitting your first issue for the Spring Framework! 👍

The behavior you have described is not a bug but rather the expected behavior.

Spring cannot execute SQL scripts without access to the DataSource/PlatformTransactionManager from your ApplicationContext.

When you use the BEFORE_TEST_CLASS mode with @Sql, the SqlScriptsTestExecutionListener has to load the ApplicationContext via the TestExecutionListener#beforeTestClass callback, which is invoked before JUnit Jupiter @BeforeAll lifecycle methods.

Since @DynamicPropertySource methods are invoked when loading the ApplicationContext, that means @DynamicPropertySource methods are invoked before @BeforeAll methods when using the BEFORE_TEST_CLASS mode.

In other words, the invocation order of those methods is required to change when using the BEFORE_TEST_CLASS mode.

Have you considered converting your @BeforeAll method to a static initialization block as follows?

static {
    System.out.println("Calling init method");

    var = 1;

    // init here mock server f.e
    // mockWebServer = new MockWebServer();
    // mockWebServer.start();
}

Comment From: sbrannen

The Javadoc for ExecutionPhase.BEFORE_TEST_CLASS currently states the following.

The configured SQL scripts and statements will be executed once per test class before any test method is run.

However, that is a bit misleading since one can infer that the SQL scripts and statements may be executed after "before class level" callbacks, which is not the case.

In light of that, I am repurposing this issue to improve the documentation.

Comment From: marwin1991

@sbrannen

Thank you for your clarification ❤️

I was thinking the order of sql BEFORE_TEST_CLASS is as follows: - method annotated with @BeforeAll - method annotated with @DynamicPropertySource - Spring is init (datasources and application context also) - SQL is executed - first test is executed

Comment From: sbrannen

@marwin1991, you can see the updated documentation here:

https://docs.spring.io/spring-framework/docs/6.1.5-SNAPSHOT/javadoc-api/org/springframework/test/context/jdbc/Sql.ExecutionPhase.html#BEFORE_TEST_CLASS

Comment From: marwin1991

@sbrannen 👍🏼