I'm writing some integration test using spring framework. I have different SQL scripts for different integration test classes. Something like this:
#ContextConfiguration(classes = ...)
#Sql("classpath:sportCenter-test.sql")
public class SportCenterResourceIT {
...
}
Everything works perfectly except for the fact that the SQL script is executed before each test, instead of one time per class. I have already searched for some time the spring documentation but I was not able to find something related to such an option.
Could anybody give me an hint?
I use this way to resolve the problem:
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
#Sql("classpath:sql/DataForRouteTesting.sql")
public class MyTest {}
Scripts are executed every time, but there are not conflict primary key.
Adding the org.springframework.transaction.annotation.Transactional annotation at the class level will prevent any changes from #Sql scripts from persisting between tests. So your code becomes:
#ContextConfiguration(classes = ...)
#Sql("classpath:sportCenter-test.sql")
#Transactional
public class SportCenterResourceIT {
...
}
This combination will result in the following:
Your #Sql script will run before each test
The test itself will run
Any database changes from steps 1 or 2 will be reverted at the end of each test
If we want to execute sql script one time per class, we can execute the script in setup method and set flag to true once the script is executed for one method so it does not execute again.
`
#Autowired
private DataSource database;
private static boolean dataLoaded = false;
#Before
public void setup() throws SQLException {
if(!dataLoaded) {
try (Connection con = database.getConnection()) {
ScriptUtils.executeSqlScript(con, new ClassPathResource("path_to_script.sql"));
dataLoaded = true;
}
}
}`
I solved my problem by putting the sql script in the src/test/resources folder with the name data.sql. This file is executed once at startup of each integration test. For this to work, you need to remove the #Sql annotation.
You can create an empty static method annotated with #BeforeAll and #Sql("classpath:sportCenter-test.sql")
#BeforeAll
#Sql("classpath:sportCenter-test.sql")
public static void initSql() {
}
Related
In order to setup test data for my Spring Boot integration tests, I'd like to create some helper classes and methods which populate the data using the repositories.
Here is an example:
#Component
public class TestUtils {
private static TemplateRepository templateRepository;
#Autowired
public TestUtils(TemplateRepository templateRepository) {
TestUtils.templateRepository = templateRepository;
}
public static void createTemplates() {
Template template = Template.builder()
.content("some content")
.build();
templateRepository.save(template);
}
}
Due to a lack of experience, I cannot tell if this approach is fine. It it "safe" to inject the repository as static? Or are there better approaches for setting up test data?
Don't use static. If you want to use Java to initialize the data in the repository, just do so in your test.
What you can do if you need to create a few things in different repositories is create a dedicated component:
#Component
public class DatabaseInitializer {
private final TemplateRepository templateRepository;
private final MyOtherRepository myOtherRepository;
// Add constructor here
public void createInitialData() {
// Use repositories to persist some data
}
#ExtendWith(SpringExtension.class)
#Import(DatabaseInitializer.class)
class MyTest {
#Autowired
private DatabaseInitializer initDb;
#Test
void myTest() {
initDb.createInitialData(); // Or put this in a `#Before..` method
// actual test here
}
}
I use TestContainers and Flyway.
You can make SQL scripts and annotate test methods with #Sql and provide a .sql file and/or statements to be run.
You can store these .sql files in the test/resources folder.
Loading Initial Test Data
There is a very well explained process to initialize the data in docs. I would advice you to refer below
https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.data-initialization
You just have to manintain Insert statements in predefined sql files.
EDIT: As C. Weber suggested in the comments, the solution is to add #Transactional to the test class.
I have some tests that use an H2 in-memory DB. I need to reset the DB before each test. Although my SQL scripts are run each a test is executed, the DB is not properly reset, resulting in a missing needed entry after a delete test.
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureTestDatabase(replace=Replace.ANY, connection=EmbeddedDatabaseConnection.H2)
public class RepositoryTests {
#Autowired
private Repository repository;
#Autowired
private DataSource dataSource;
#Before
public void populateDb() {
Resource initSchema = new ClassPathResource("database/schema.sql");
Resource initData = new ClassPathResource("database/data.sql");
DatabasePopulator dbPopulator = new ResourceDatabasePopulator(initSchema, initData);
DatabasePopulatorUtils.execute(dbPopulator, dataSource);
}
#Test
public void testMethod1() {
// ...
repository.delete("testdata");
}
#Test
public void testMethod2() {
// ...
Object test = repository.get("testdata");
// is null but should be an instance
}
}
schema.sql drops all tables before recreating them. data.sql inserts all needed test data into the DB.
Running the testMethod2 alone succeeds. However, running all tests makes the test fail with a NullPointerException.
I have successfully tried to use #DirtiesContext, however this is not an option because I can't afford to have a 20 second startup for each 0.1 second test.
Is there another solution?
The Spring Test Framework provides a mechanism for the behaviour you want for your tests. Simply annotate your Test class with #Transactional to get the default rollback behaviour for each test method.
There are ways to configure the transactional behaviour of tests and also some pitfalls (like using RestTemplate inside test method), which you can read more about in the corresponding chapter of the Spring manual.
Spring Test Framework
I'm trying to combine the follow annotations:
org.springframework.test.context.jdbc.Sql
and
org.junit.Before
Like the follow code:
#Test
#Sql(scripts = "dml-parametro.sql")
public void testData(){
Iterable<Parametro> parametros = parametroService.findAll();
List<Parametro> parametrosList = Lists.newArrayList(parametros);
Assert.assertThat(parametrosList.size(), Is.is(1));
}
#Before
public void beforeMethod() {
JdbcTestUtils.deleteFromTables(jdbcTemplate, "PARAMETRO");
}
The code in the method #Before is running after then the script "dml-parametro.sql" in the #Sql annotation.
Is it right to do this?
For solution this, I'm using #After in place than #Before, but I'd like to cdelete tables before the test execution, not after.
I wouldn't like to use #SqlConfig. I'm not using transacional scope on test level, so i need to clean my tables in every test method. If every test method need to clean tables, i would like to do this in #Before method. I wouldn't like to do this in every test method with #SqlConfig. I think the behavior of #Sql to be execute before than #Before is wrong.
By default, any SQL scripts executed via #Sql will be executed before any #Before methods. So the behavior you are experiencing is correct, but you can change the execution phase via the executionPhase attribute in #Sql (see example below).
If you want to execute multiple scripts, that is also possible via #Sql.
So if you have a clean-up script named clean-parametro.sql that deletes from the PARAMETRO table, you could annotate your test method like the following (instead of invoking JdbcTestUtils.deleteFromTables() in your #Before method).
#Test
#Sql({"dml-parametro.sql", "clean-parametro.sql"})
public void test() { /* ... */ }
Of course, if dml-parametro.sql inserts values into the PARAMETRO table, then it likely does not make sense to immediately delete those values in the clean-up script.
Please note that #Sql and #SqlConfig provide multiple levels of configuration for script execution.
For example, if you want to create tables before your test and clean up after your test, you could do something like this on Java 8:
#Test
#Sql("create-tables.sql")
#Sql(scripts = "clean-up.sql", executionPhase = AFTER_TEST_METHOD)
public void test() { /* ... */ }
Or use #SqlGroup as a container on Java 6 or Java 7:
#Test
#SqlGroup({
#Sql("create-tables.sql"),
#Sql(scripts = "clean-up.sql", executionPhase = AFTER_TEST_METHOD)
})
public void test() { /* ... */ }
If your tests are #Transactional and you'd like to clean up committed database state, you can instruct Spring to execute your clean-up SQL script in a new transaction like this:
#Test
#Sql("insert-test-data.sql")
#Sql(
scripts = "clean-up.sql",
executionPhase = AFTER_TEST_METHOD,
config = #SqlConfig(transactionMode = ISOLATED)
)
public void test() { /* ... */ }
I hope this clarifies things for you!
Cheers,
Sam (author of the Spring TestContext Framework)
Notes:
AFTER_TEST_METHOD is statically imported from ExecutionPhase
ISOLATED is statically imported from TransactionMode
I have a test like this:
#RunWith(SpringJUnit4ClassRunner.class),
#ContextConfiguration(locations = { "file:war/WEB-INF/application-context.xml" })
#Transactional
public class ServiceImplTest extends AbstractTestNGSpringContextTests
{
#Autowired
private Service service;
#Test
#Rollback(false)
public void testCreate()
{
.....
//save an entity to table_A
service.save(a);
}
}
It seems that the table_A will be cleaned up before each test running(not roolback after test ran),because after each test,all old data entries in the table are cleaned up,only new inserted entry by test is left.How to prevent this "cleaning" action?
The default behavior is to rollback the transactions in testing context. You can override this behavior using the #Rollback(false) annotation on a test method to not rollback the changes made to the DB during that particular test.
That said, it is recommended that each test case is independent and should have its own scenario setup, scenario execution and scenario tear down. Otherwise, the test failure behavior would be difficult to analyze if there are inter-dependencies among tests.
I have a junit 4 test class testing a DAO.
unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:/WEB-INF/applicationContext-db.xml",
"classpath:/WEB-INF/applicationContext-hibernate.xml",
"classpath:/WEB-INF/applicationContext.xml" })
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
#DataSetLocation("test/java/com/yada/yada/dao/dbunit-general.xml")
#TransactionConfiguration(transactionManager="transactionManager", defaultRollback = true)
#Transactional
public class RealmDAOJU4Test {
#Autowired
private DbUnitInitializer dbUnitInitializer;
#Autowired
private RealmDAO realmDAO;
#BeforeTransaction
public void setupDatabase() {
// use dbUnitInitializer to insert test data
}
#Test
public void testGetById() {
Integer id = 2204;
Realm realm = realmDAO.get(id);
assertEquals(realm.getName().compareToIgnoreCase(
"South Technical Realm"), 0);
assertEquals(8, realm.getRealmRelationships().size());
}
// more test methods annotated here
}
The #BeforeTransacation method runs before EVERY test method. What I would like to do is: use my DbUnitInitializer to load data into my database - ONCE when the class is created. Then have each test in the class do what it needs to do with the database, then roll back (not commit) it's changes. It seems over kill to re-insert all the same data from my test files before EVERY test. Is there a way to accomplish this?
or
Is the correct way to write these tests to completely load the database before EVERY test? If so, what function does the defaultRollback=true have in this situation?
thanks for helping me along in my thinking...
You need to use a TestExecutionListener and set up your database in the beforeTestClass method. See the Annotations section of the Testing chapter in the Spring user guide.