How to execute sql script before and after each test method - java

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();
}
}

Related

Spring Data JPA database changes after each method don't roll back in integration tests

I am using JUnit 5 with Spring boot 2.2.4 for integration testing my Spring Boot application code with MySQL 5.7 database but the changes that a previous test method makes to database aren't rolled back causing errors in the ones that follow.
I've tried answers from different stackoverflow posts such as adding #Transactional (from Spring), #Rollback (method as well as class level), #TestExecutionListeners(listeners = {TransactionalTestExecutionListener.class}). I've tested these options individually (as well as in combination) but adding them in the test code together.
This is my test code:
#DataJpaTest
#Transactional
#Rollback
#ExtendWith(SpringExtension.class)
#Import({UserUsecasesImpl.class})
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserUsecasesImplINTTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
#Spy
private UserRepository userRepository;
#Autowired
private UserUsecases userUsecases;
#Test
void injectedComponentsAreNotNull(){
assertNotNull(entityManager);
assertNotNull(userRepository);
assertNotNull(userUsecases);
}
#Test
#Rollback
void Create_EntityAlreadyExists_ReturnsException() {
// set up
User expected = UserFixture.user1.toBuilder().build();
// entityManager.persistAndFlush(expected);
userRepository.saveAndFlush(expected);
// execute
User actual = userUsecases.create(expected);
// verify
assertEquals(expected, actual);
verify(userRepository).findOne(any(Example.class));
verify(userRepository, never()).save(any(User.class));
verifyNoMoreInteractions();
}
#Test
void Find_WhenUserPresent_ReturnUser() {
// // set up
User expected = UserFixture.user1.toBuilder().build();
entityManager.persistAndFlush(expected);
// execute
User actual = userRepository.find(expected);
// verify
assertEquals(expected, actual);
}
#Test
// #Transactional
void Find_WhenUserNotPresent_ReturnNull() {
// // set up
User expected = UserFixture.user1.toBuilder().build();
// entityManager.persistAndFlush(UserFixture.user2.toBuilder().build());
// execute
User actual = userUsecases.find(expected);
// verify
assertNull(actual);
}
}
UserUsecasesImpl is the class that I am trying to test which contains usecases such as creating a user, finding a user, get all users, etc. There is no special configuration anywhere else in the code.
Please implement following fixes and let me know if problem still there:
Remove all #Transactional and #Rollback. Keep #DataJpaTest which by default makes all of your tests #Transactional and therefore by default will roll them all back.
don't use saveAndFlush. The flush part is flushing save operation into the database and most likely causes your issue. Use save instead. More here https://www.baeldung.com/spring-data-jpa-save-saveandflush
If you are expecting exception assert it with assertThrows.
In general try not to throw every single annotation you know into the mix at the same time. Add them one at a time as you need them, try to understand what they do.
What is UserUseCases and why does it do most of the logic I would expect the Repository class to do?

How to cleanup h2 db after each junit test?

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

How to get JPA context in Springboot integration test?

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.

Run code after all class #Test methods but before #AfterGroups in TestNG

I want to test some database-related classes in my application using TestNG framework.
To make it easier I added a test group "database" and made a test class TestUtil, which contains two methods: one with #BeforeGroups(groups = "database") annotation, it's setting up EntityManager and some other resources, and another one marked with #AfterGroups(groups = "database"), which frees up these resources.
Most of my test classes persisting some entities to database during the test, and I want to clean up the DB after all test methods of the class are invoked.
If I use #AfterClass annotation, it runs after the #AfterGroups methods, what is unacceptable for me because clean-up method still needs active EntityManager and other DB-related resources.
I can mark those clean-up methods with #Test(dependsOnMethods = "lastTestMethodInThisClass"), but in this case I'll need to edit this annotation each time I add new test method to the class.
Is there another, more convenient way to do this job?
No, there is no such annotation which allows you to run code after all #Test and before #AfterGroups. But instead of #Test(dependsOnMethods = "lastTestMethodInThisClass") you can implement org.testng.IMethodInterceptor as listener and change #Test order in org.testng.IMethodInterceptor#intercept - basically search your 'last test' in the list put your 'last test' which handles/closes resources to the end and return modified list. So you don't have to change dependsOnMethods when you add new test because interceptor do it for you.
If you want to run a cleanup method after all the tests are invoked, you can write your code in your cleanup method with #AfterSuite annotation. This will be executed after the whole suite has finished execution.

How do you clear objects between JUnit/Maven tests?

I'm testing different parts of a miniature search engine, and some of the JUnit tests are leaving entries in the index that interfere with other tests. Is there a convention in JUnit/Maven for clearing objects between tests?
There are 2 particular annotations that can help you with this, and are intended to be used in cases such as yours:
#After defines that a certain method must be executed after every #Test, while #AfterClass is the method to execute once the entire test class has been executed. Think of the latter as a last cleanup method to purge any structures or records you've been using and sharing between tests so far.
Here is an example:
#After
public void cleanIndex() {
index.clear(); //Assuming you have a collection
}
#AfterClass
public void finalCleanup() {
//Clean both the index and, for example, a database record.
}
Note: They have their counterparts (#Before and #BeforeClass) that to exactly the opposite by invoking the related methods before a #Test method and before starting to execute the #Tests defined on that class. This ones are the setUp used in previous versions of JUnit.
If you can't use annotations, the alternative is to use the good old tearDown method:
public void tearDown() {
index.clear(); //Assuming you have a collection.
}
This is provided by the JUnit framework and behaves like a method annotated with #After.
You should make use of the #Before annotation to guarantee that each test is running from a clean state. Please see: Test Fixtures.
Inside of your junit testing class, you can override the methods setup and teardown. setup will run before every one of your tests while teardown will run after every single junit test that you have.
ex:
public class JunitTest1 {
private Collection collection;
//Will initialize the collection for every test you run
#Before
public void setUp() {
collection = new ArrayList();
System.out.println("#Before - setUp");
}
//Will clean up the collection for every test you run
#After
public void tearDown() {
collection.clear();
System.out.println("#After - tearDown");
}
//Your tests go here
}
This is useful for clearing out data inbetween tests, but also allows you to not have to reinitialize your fields inside of every single test.

Categories

Resources