I'm building a REST service using Spring boot framework (v2.0.2), where I have integration tests running on a database. I googled a lot and there is a sea of articles about cleaning database before tests, but unfortunately I find them either inefficient or a hack and not for Spring boot. Could you, please, bear with me and suggest a good way for this problem?
Ideally, I think the database should be cleared not before each test, but before some group of them, like suite or maybe each test class. One of the found suggestions looks like this:
@Autowired
protected Flyway flyway;
@Before
public void init() {
flyway.clean();
flyway.migrate();
}
which rebuilds database before each test and clearly is not efficient. Changing this to static context and using @BeforeClass
does not work as Spring does not inject static fields.
Is there some nice way to reach this flyway bean from static context, to make this solution work?
Sub-question here: Flyway has a command clean, which not only clears data, but drops everything and then migrate command performs migrations again. This also seems like overhead. As the migrations are checked at startup anyway, I don't see the necessity to tear down and rebuild everything before each test group. Just clearing data would be sufficient. Would you give some advice about how this can be achieved?
To sum up, I'm looking for a standard way of removing the database data (and not tables if possible) before each group of integration tests (e.g. per class). I suppose everybody faces this task while using Spring boot, so maybe there is some nice solution considered in the framework itself.
Thank you!
Flyway updates a database from one version to the next using migrations. We can write migrations either in SQL with database-specific syntax, or in Java for advanced database transformations.
The @SpringBootTest annotation loads the complete Spring application context. In contrast, a test slice annotation only loads beans required to test a particular layer. And because of this, we can avoid unnecessary mocking and side effects.
You could create configuration file for your tests. It would run one time before all tests.
@Configuration
public class TestConfig {
@Bean
public FlywayMigrationStrategy clean() {
return flyway -> {
flyway.clean();
flyway.migrate();
};
}
}
This answer was useful but it didn't get me all the way there and so I thought I'd come back and add an answer in case someone else was looking to solve this same issue. The bean definition above was terrific.
There are spring profiles of which there are 5 or so possibilities. I looked at the docs and how people use them but went another route. Maven has 6 scopes, but the ones which are useful in this case are runtime and test.
As I dug into the spring profiles and the various ways one can switch between them it seemed for my situation a bit too complex. I just want my database under test to be created, scructured and populated with some data so I can test the repositories in my jpa spring boot app. I don't want to spend 4 hours setting up profiles. Not that it isn't a worthy endeavor in the long run, just that I wanted to get things moving.
When I execute spring-boot:run, I want the non-test db to be migrated but I don't want any crud data in there that I use for testing.
So, in the live app I want a virtually empty database and during tests, I want flyway to clean the db, run the versioned migrations and populate it with test data.
The answer above led me to a solution which I will probably fold into spring profiles as my project gets closer to production.
Turns out that spring-boot-test provides a @TestConfiguration annotation which you can attach to any class in the src/test/ hierarchy. I created a FlywayConfiguration class which contains the bean definition provided above:
package com.foo.fooservice;
import org.flywaydb.core.Flyway;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
@TestConfiguration
public class FlywayMigrationConfig {
@Bean
public static FlywayMigrationStrategy cleanMigrateStrategy(){
return flyway -> {
flyway.clean();
flyway.migrate();
};
}
}
So now, if I want to use this in a test, I add another nifty annotation in the appropriate test class- @Includes, a companion to the @TestConfiguration annotation - so that I can use this configuration in the same way I might have used @BeforeClass like so:
@DataJpaTest
@Import(FlywayMigrationConfig.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class AccountUserRepoTest {
@Autowired
private AccountUserRepo accountUserRepo;
@Autowired
private FlywayMigrationStrategy strategy;
This allows me to inject this flyway migration strategy on a per-test-class basis. Spring won't auto-inject your bean into every test class and you can use this configuration now just by adding the @Includes annotation to the appropriate test classes. You don't have to define the bean in every test class where you want to use it. Just use @Includes(your@TestCongiguration-annoted-class).
I happen to be using postgresSQL as opposed to H2 because I figure if I'm doing an integration test on the repository entities I might as well do it against what I will be using in production.
Also: src/main/resources has the jdbc and flyway properties set to a dev schema name and jdbc url.
src/test/resources/application.properties sets the schema name to 'test' (you can name it whatever you want).
The one drawback to this approach that you may not want is the granularity- the DB is cleaned and repopulated for every test class which you configure this way.
I personally like this because for each repository class I'm testing I'd like the data to be refreshed. I also like that if I'm working on a particular test class, having the configuration at this level of granularity means that 'run test' works out of the box. No special configuration in the IDE is required to make it work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With