I have a lot of existing Spring JUnit tests.
All this tests extends an abstract test class.
public class ServiceTest extends AbstractServiceTest {
But in this abstract class we reinitialize the database. So it will reinitialize the database on each test class
#Before
#Override
public void initGlobal() throws Exception {
initDatabase();
... }
I am asking how I can do a rollback on a test class in the end of execution of its tests ? So i can initialize a database one time and rollback changes in every test class
I think having two profiles is a better option one for testing and other one for development and on the testing profile use memory based database like H2 (here is a good example) and on your development profile use your main database
When running the tests use the testing profile.Instead of doing rolling back or deleting data each time you run your test
If you want to use a real database in unit tests that I totally discourage it.You can use the spring test runner to annotate your class and rollback transactions
#RunWith(SpringJUnit4ClassRunner.class)
#TransactionConfiguration(defaultRollback=true)
public class YourTestClass {
#Test
#Transactional
public void myTestMethod() {
// db will get rolled back at the end of the test
}
}
Related
There is an #Sql annotation in spring which allows to execute sql code before and after the test method:
#Test
#Sql("init.sql")
#Sql(scripts = "clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void someTest()
{
}
However I have several test methods where I would like to provide the same clean environment like in the test above and I don't want to repeat for every test the same #Sql annotation. How to do it once for all methods? For example:
// JPA and Spring other test annotations
#Sql("init.sql")
#Sql(scripts = "clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public class TestClass
{
// init.sql executed before every test, clean.sql executed after every test
}
Indeed when you place #Sql on the class, sql scripts will be executed before and after every test defined in that class, more specifically before #Before and after #After methods. So,
// JPA and Spring other test annotations
#Sql("init.sql")
#Sql(scripts = "clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public class TestClass
{
// init.sql executed before every test, clean.sql executed after every test
}
is going to work according to the #Sql definition:
#Target({ElementType.TYPE, ElementType.METHOD})
/// other annotations
public #interface Sql {
//
}
If my understanding is correct, you want to execute an init script to put the DB in a certain state, and then ensure the DB is back to that state before each test method, right?
The simplest solution is to use #Transactional, then. By default, Spring Boot will automatically roll back the test transaction of a #Transactional-annotated test, thus resetting the DB to the original state.
There are two downsides one should consider, though:
#Transactional means there will exist a transaction spanning the entire execution of the test method which the tested service methods will typically join. Hence, the test itself cannot be relied upon to verify the correctess of transactional boundaries in the production code (LazyInitializationExceptions may be covered by this 'outer' transacion, for example)
The persistence context will not flush unless necessary, meaning that some issues (e.g. DB constraint violations) will not surface. I tend to work around that issue using a last-chance flush like so:
#After
public void flushContext() {
if (TransactionSynchronizationManager.isActualTransactionActive()) {
entityManager.flush();
}
}
I am making junit tests which adds some users before each test case. The code is:
#BeforeEach
void setUp() {
saveUser();
saveEntry();
}
#Test
void saveUser() {
User user = new User();
user.setUserId(null);
user.setUsername("John");
user.setEmail("john#foo.com");
user.setPassword("password");
userService.saveUser(user);
}
#Test
void saveEntry() {
Entry entry = new Entry();
entry.setText("test text");
entry.setUserId(1L);
entryService.saveEntry(entry);
}
As you see I am using the methods that I have in my service layer to create entries and users. If I run the tests one by one there is no problem. But when I run all tests then db is not returning 1 item and returning multiple items so exception occurs.
I need to cleanup h2 db after each test with maybe #AfterEach annotation but I do not have and delete method in my code to invoke. How can I cleanup the H2 db ?
In addition to #J Asgarov answer which is correct providing you use spring-boot if you want to perform some actions before and after each test (more specifically before #Before and after #After methods) you can use #Sql annotation to execute specific sql script for example from test resources.
#Sql("init.sql")
#Sql(scripts = "clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public class TestClass
}
It might be handy for sequences, since they don't care of rollback.
Regarding #Transactional mentioned by #Mark Bramnik be careful, because the transaction spans for entire test method, so you cannot verify correctness of the transaction boundaries.
You've mentioned spring and it looks like you're testing DAO layer, so I assume the tests are running with SpringExtension/SpringRunner if you're on junit 4.
In this case,
Have you tried to use #Transactional on the test method? Or alternatively if all the test methods are "transactional" you can place it once on a test class.
This works as follows:
If everything is configured correctly spring will open a transaction before the test starts, and will rollback that transaction after the test ends.
The rollback is supposed to clean the inserted data automatically.
Quick googling revealed this tutorial, surely there are many others
#JpaDataTest annotating the test class will rollback every test by default
https://www.baeldung.com/spring-jpa-test-in-memory-database
I am running a Springboot application with jpa. I am trying to set up an integration-test based on Cucumber. When in my test I try to access the repo, I get an 'org.hibernate.LazyInitializationException' (With a message "No Session".). This only happens in my integration-test, but not in the real application. A workaround would be to put #Transactional on the method which does the call, but this does not work if I execute in new threads.
My first question would be: Why doesn't it work withouth #Transactional annotation?
My second would be: Why doesn't it work with #Transactional annotation in new threads?
Here is a simplified version of my code:
The cucumber test:
#RunWith(Cucumber.class)
#CucumberOptions(features = "src/test/resources/some-integration-test.feature")
public class IntegrationTests {}
The cucumber-steps:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
public class IntegrationSteps {
#Autowired
SomeRepo repo;
#When("two updates happen at the same time")
public void twoUpdatesHappenAtTheSameTime() {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(set("Thread 1"));
executorService.execute(set("Thread 2"));
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
}
public void set(String someThing) {
Some some = repo.getOne(1234);
repo.setSomeThing(someThing);
repo.save(some);
}
}
And the repo:
#Repository
public interface SomeRepo extends JpaRepository<Some, Integer> {}
The problem seems to be with getOne(). With getOne() you only get a reference to the entity. No real call to the database is executed until one tries to access the entity's fields.
Normally these fields are loaded lazy when one uses getOne(), but for some reason (which is still unclear to me) this does not work in the SpringBootTest.
I've found two solutions for this issue:
Annotate the test with #Transactional. This way you will have a context to load the entity. The downside is that you still don't seem to get the most recent version of your entity. In my case I updated the entity in my code, while in test code the update was not present in the entity (even though update happened before the getOne() call).
Don't use getOne(), but findById() instead. The downside is that findById() is eagerly loaded, but as we only use this for our test, it will not affect the performance of our app.
I want to run serveral unit test and functional test on my Play Framework App(Play 1.4.0). A Test class like:
public class StatisticTest extends UnitTest {
#Test
public void testOutputStatistics() {
//code
}
}
And I have some data in my database which the test class need to read. But the Play test will clear all data in the database automatically. I have to import my data via SQL by using Fixtures.executeSQL() every time. This lowers the efficiency. So I want to know how can I avoid this auto clear.
In application.conf, set the test.jpa.ddl as "update" rather than "create".
%test.jpa.ddl=update
Lets say I have a test class called ServiceTest with three test methods test1, test2 and test3. All three methods use some resources which are provided by spring. In the current state of affairs, if there is a problem with the loading of the spring context, the context load is retried for each test method. Is there a way that I can have it aborted on the first failure itself with existing hook-ins? There might be good reasons behind it - but I fail to appreciate it. Any pointers would be helpful. The real problem is that the context load takes a few minutes and is useless to keep reattempting to load context if it has already failed for the first time and only seeks to prolong the time that the CI engine takes to report failures.
I was thinking of proposing a patch to spring guys that can maintain an attempt map in org.springframework.test.context.TestContext which can be used to track attempts made and refrain from trying over and over again. Thoughts?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:com/kilo/spring/test-context.xml" })
public class ServiceTest {
#Resource(name = "fibonacciService")
private FibonacciService fibonacciService;
#Test
public void test1() {
long fibonacci = fibonacciService.getFibonacci(5);
}
#Test
public void test2() {
long fibonacci = fibonacciService.getFibonacci(4);
}
#Test
public void test3() {
long fibonacci = fibonacciService.getFibonacci(6);
}
No, there is currently no way to have ApplicationContext loading aborted on the first failure with existing hook-ins.
Feel free to create a JIRA issue against the SPR project and the Test component with your suggestions.
Regards,
Sam (author of the Spring TestContext Framework)