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
Related
Please give me tips to get my failing JUnit test to pass in my Spring Boot app.
I have a Service that needs to delete rows from tables A and B (from a H2 database) in a transactional manner such that: if A is modified and then an exception occurs before B is modified, then neither A nor B are modified. However, even after
Adding #Transactional to the PUBLIC Service::deleteRows method, and
Adding #EnableTransactionManagement to the #Configuration class that produces the JdbcTemplate that all repositories use, and
Confirming that spring-tx is on the classpath
the transaction still doesn't rollback.
Here's the failing JUnit testcase
// Service.java
#Transactional
public void deleteRows() {
aRepository.deleteRow(123);
bRepository.deleteRow(123);
}
// ServiceTest.java
#Test
void test() {
// first ensure that an exception is thrown when bRepository.deleteRow() is called
// FYI deleteRow() calls jdbcTemplate.update()
doThrow(RuntimeException.class)
.when(mockJdbcTemplate)
.update(any(), eq(123));
BRepository mockBRepository = new BRepository(mockJdbcTemplate);
Service service = new Service(realARepository, mockBRepository);
assertTableHasRows(1, "TABLE_A"); // before calling the service, TABLE_A has 1 row
assertThrows(RuntimeException.class, service::deleteRows);
assertTableHasRows(1, "TABLE_A"); // TABLE_A should still have 1 row when an exception occurs during the tx
}
Remove #Transactional and run test again.
In the end the problem was solved by NOT using mocking to trigger an exception. Instead, we set up the database schema so that normal operation of service::deleteRows would trigger an exception.
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?
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 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.
I annotate my test methods like this:
#Test
#DatabaseSetup("/default_database_data.xml")
#ExpectedDatabase(value = "/expected_database_1.xml", assertionMode = NON_STRICT)
is it possible to manually perform the things that #DatabaseSetup and #ExpectedDatabase does:
#Test
public void test(){
// DBUnit.setup("/default_database_data.xml");
dao.insert(...);
// DBUnit.expected("/expected_database_1.xml");
}
I made the syntax up, just to give you an idea of what I need: perform 2 setups and assertions in one unit test.
two things that might work, check this link.
Link
And also this annotation:
#DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)