Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can Spring's test annotation @Sql behave like @BeforeClass?

How can I tell the @Sql annotation to run only once for the class, and not for each @Test method?

Like having the same behaviour as @BeforeClass?

@org.springframework.test.context.jdbc.Sql(
     scripts = "classpath:schema-test.sql",
     executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD
)
public class TestClass {
      @Test
      public void test1() {
        //runs the @Sql script
      }

      @Test
      public void test2() {
        //runs the @Sql script again
      }
}
like image 278
membersound Avatar asked Dec 12 '17 14:12

membersound


People also ask

Which annotation is used for testing Spring MVC which apply only the configuration related to MVC test?

Again, very similar to the @DataJpaTest and the @DataMongoTest annotations, to perform classic Spring MVC tests, we apply the @WebMvcTest annotation alongside the @RunWith(SpringRunner. class) annotation. Keep in mind that the effects of this annotation only apply to the MVC infrastructure.

What kind of testing can be done in spring test module?

The core items for the testing are contained in the modules called spring-boot-test and the configuration is provided by the modules called spring-boot-test-autoconfigure. We can simply use the spring-boot-starter-test in pom. xml and transitively pull all the required dependencies in a Spring application.

Which annotation is provided to load a spring container in application JUnit test?

We do this using JUnit's @RunWith annotation. We also annotate our test class with Spring's @ContextConfiguration annotation without specifying any attributes.


1 Answers

For JUnit 5, the straight forward clean solution:

@MyInMemoryDbConfig
//@Sql(value = {"/appconfig.sql", "/album.sql"}) -> code below is equivalent but at class level
class SomeServiceTest {
    @BeforeAll
    void setup(@Autowired DataSource dataSource) {
        try (Connection conn = dataSource.getConnection()) {
            // you'll have to make sure conn.autoCommit = true (default for e.g. H2)
            // e.g. url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1;MODE=MySQL
            ScriptUtils.executeSqlScript(conn, new ClassPathResource("appconfig.sql"));
            ScriptUtils.executeSqlScript(conn, new ClassPathResource("album.sql"));
        }
    }
    // your @Test methods follow ...

but when your database connections are not configured with autoCommit = true you'll have to wrap all in a transaction:

@RootInMemoryDbConfig
@Slf4j
class SomeServiceTest {
    @BeforeAll
    void setup(@Autowired DataSource dataSource,
            @Autowired PlatformTransactionManager transactionManager) {
        new TransactionTemplate(transactionManager).execute((ts) -> {
            try (Connection conn = dataSource.getConnection()) {
                ScriptUtils.executeSqlScript(conn, new ClassPathResource("appconfig.sql"));
                ScriptUtils.executeSqlScript(conn, new ClassPathResource("album.sql"));
                // should work without manually commit but didn't for me (because of using AUTOCOMMIT=OFF)
                // I use url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1;MODE=MySQL;AUTOCOMMIT=OFF
                // same will happen with DataSourceInitializer & DatabasePopulator (at least with this setup)
                conn.commit();
            } catch (SQLException e) {
                SomeServiceTest.log.error(e.getMessage(), e);
            }
            return null;
        });
    }
    // your @Test methods follow ...

Why clean solution?

Because according to Script Configuration with @SqlConfig:

The configuration options provided by @Sql and @SqlConfig are equivalent to those supported by ScriptUtils and ResourceDatabasePopulator but are a superset of those provided by the XML namespace element.

Bonus

You can mix this approach with other @Sql declarations.

like image 77
adrhc Avatar answered Sep 19 '22 21:09

adrhc