Clear Spring application context after test - spring

How can I clear the application context after each test execution, with Junit5 and Spring Boot? I want all beans created in the test to be destroyed after its execution, since I am creating the same beans in multiple tests. I don't want to have one configuration class for all tests, but configuration class per test, as shown bellow.
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = MyTest.ContextConfiguration.class)
public class MyTest{
...
public static class ContextConfiguration {
// beans defined here...
}
}
Putting #DirtiesContext(classMode = BEFORE_CLASS) doesn't work with Junit5.

You have claimed twice that #DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) does not work; however, the following shows that it works as documented.
import javax.annotation.PreDestroy;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
#ContextConfiguration
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
class MyTest {
#Test
void test1(TestInfo testInfo) {
System.err.println(testInfo.getDisplayName());
}
#Test
void test2(TestInfo testInfo) {
System.err.println(testInfo.getDisplayName());
}
#Configuration
static class Config {
#Bean
MyComponent myComponent() {
return new MyComponent();
}
}
}
class MyComponent {
#PreDestroy
void destroy() {
System.err.println("Destroying " + this);
}
}
Executing the above test class results in output to STDERR similar to the following.
test1(TestInfo)
Destroying MyComponent#dc9876b
test2(TestInfo)
Destroying MyComponent#30b6ffe0
Thus, #DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) is indeed how you "clear the application context after each test execution".
Putting #DirtiesContext(classMode = BEFORE_CLASS) doesn't work with Junit5.
Based on my testing, classMode = BEFORE_CLASS works with TestNG, JUnit 4, and JUnit Jupiter (a.k.a., JUnit 5).
So, if that does not work in your test class, that would be a bug which you should report to the Spring Team.
Do you have any example where you can demonstrate that not working?
FYI: using classMode = BEFORE_CLASS would only ever make sense if the context had already been created within the currently executing test suite. Otherwise, you are instructing Spring to close and remove an ApplicationContext from the cache that does not exist... just before Spring actually creates it.
Regards,
Sam (author of the Spring TestContext Framework)

According to the docs, try #DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)

Related

I can't test on a test database during my integration tests

I would like to do some integration tests. These tests would then use a h2 test database, which would always be deleted afterwards.
Here is my test:
#SpringBootTest()
public class PostePomponServiceIT{
#Autowired
private PostePomponService postePomponService;
#Autowired
private PostePomponRepository postePomponRepository;
#Test
public void addPostePompon_Ok() throws BadRequestException {
PostePomponForm postePomponForm = new PostePomponForm();
postePomponService.add(postePomponForm);
assertEquals(1[![enter image description here][1]][1], postePomponRepository.findAll().size());
}
}
and my main test class:
package com.MailleCoTech.SuiviProduction;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.PropertySource;
#SpringBootTest(classes = SuiviProductionApplication.class)
#PropertySource("application-test.properties")
class SuiviProductionApplicationTests {
#Test
void contextLoads() {
}
}
My test-application.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
and how my folder is structured:
My error is : com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
Remove annotation #SpringBootTest from test case. You have #DataJpaTest which does everything #SpringBootTest + some extra stuff(you can check documentation for more details).
The problem here is that you are using an empty #SpringBootTest.
but you also need to say the test which configuration classes you want to execute. I mean you say to the test "please execute a spring test", but you are not providing any spring application to it.
#SpringBootTest(classes={YourJavaClassWithStaticMain.class})
is what you want to do

How do I annotate my JUnit test so it will run the way my Spring Boot app runs?

I have a Spring Boot web application that I launch by running this class ...
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The web app has a JSP/HTML front end served up by Spring MVC Controllers which talk to Services which talk to DAOs which uses Hibernate to read/write Entities to a MySQL database.
All the components and services get instantiated and #Autowired and the web app runs fine.
Now, I want to build JUnit tests and test some of the functionality in the Services or the DAOs.
I started writing a JUnit test like below, but I quickly got stuck on not knowing how to instantiate all the #Autowired components and classes.
public class MySQLTests {
#Test
public void test000() {
assertEquals("Here is a test for addition", 10, (7+3));
}
#Autowired
UserService userService = null;
#Test
public void test001() {
userService.doSomething("abc123");
// ...
}
}
I basically want the web application to start up and run, and then have the JUnit tests run the methods in those Services.
I need some help getting started ... is there some kind of JUnit equivalent of the #SpringBootApplication annotation that I can use in my JUnit class?
Answering my own question ... I got it working like this ...
Annotated the test class with:
#RunWith(SpringRunner.class)
#SpringBootTest
The test class had to be in a package above the #Controller class ... so my test class is in com.projectname.* and the controller is is com.projectname.controller.*
The working code looks like this ...
package com.projectname;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.projectname.controller.WebController;
#RunWith(SpringRunner.class)
#SpringBootTest
public class Test1 {
#Autowired
private WebController controller;
#Test
public void contextLoads() throws Exception {
assertNotNull("WebController should not be null", controller);
}
}

#Autowired should not work without #RunWith(SpringRunner.class) but does

Here is a unit testing class for a java spring data repository layer.
I have a spring data repository layer in which the annotation #Autowired is used to inject TestEntityManager type object (belongs to spring data package).
The autowiring works whitout adding #RunWith(SpringRunner.class) annotation !
So what is the problem ? Well, I think that injection should not be possible whitout adding #RunWith(SpringRunner.class) annotation to the class : it should not work without it theorically.
How is it possible ? Does someone have any answer ?
>>>> view complete spring boot app code on github available here
Someone else have had the opposite problem in stackoverflow :
Someone else have had the opposite problem : see link here
Here is my strange bloc of code that amazingly that works :
package org.loiz.demo;
import org.assertj.core.api.BDDAssertions;
import org.junit.Assert;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test ;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.Order ;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import demo.LoizBootSpringDemoApplication;
import demo.crepository.UserRepositoryInterface;
import demo.dmodel.User;
import demo.helper.helperUtils;
//Test de la couche de persistence
//#RunWith(SpringRunner.class)
#DataJpaTest
#ContextConfiguration(classes = { LoizBootSpringDemoApplication.class})
#TestMethodOrder(MethodOrderer.OrderAnnotation.class)
#SuppressWarnings("unused")
public class LoizPersistenceTest
{
#Autowired
private TestEntityManager testEntityManager;
#Autowired
private UserRepositoryInterface repository;
private static Long idStub ;
#Test
#Order(1)
#Rollback(false)
#DisplayName("Test de sauvegarde d\'un user \"prenom21 Nom21\"")
public void saveShouldMapCorrectly() throws Exception {
User userStub = new User("prenom21", "Nom21");
User UserSaved = this.testEntityManager.persistFlushFind(userStub);
BDDAssertions.then(UserSaved.getId()).isNotNull();
idStub = UserSaved.getId() ;
User UserRead = this.testEntityManager.find(User.class, idStub) ;
BDDAssertions.then(UserSaved.getFirstName()).isNotBlank();
BDDAssertions.then(UserSaved.getFirstName()).isEqualToIgnoringCase("prenom21");
BDDAssertions.then(UserSaved.getLastName()).isEqualToIgnoringCase("Nom21");
BDDAssertions.then(UserSaved.getLastName()).isNotBlank();
}
#Test
#Order(2)
#DisplayName("Test d'existence du user \"prenom21 Nom21\"")
public void readShouldMapCorrectly() throws Exception {
User userStub = new User(idStub, "prenom21", "Nom21");
User userFetched = this.testEntityManager.find(User.class, idStub) ;
String sUserStub = userStub.toString() ;
String sUserFetched = userFetched.toString() ;
boolean bolSameObject = userStub.equals(userFetched) ;
boolean bolAttrEgalEgal = sUserStub == sUserFetched ;
boolean bolStringEqual = sUserStub.equals(sUserFetched) ;
boolean bBeanUtil = helperUtils.haveSamePropertyValues (User.class,userStub,userFetched) ;
Assert.assertTrue(bBeanUtil);
}
}
Maybe it is normal, but what do i have to know ?
I would appreciate some answer please :)
>>>> view complete spring boot app code on github available here
#RunWith(SpringRunner.class) is used for junit 4 test.
But in our case, it is Junit 5 that is used. That is what we can see reading at the pom.xml file (using of junit.jupiter artifact proves that).
So #RunWith(SpringRunner.class) has no effects to manage spring container.
it should be replaced by #ExtendWith(SpringExtension.class).
HOWEVER :
#DataJpaTest already "contains" #ExtendWith(SpringExtension.class) !!!
So we don't need to add #ExtendWith(SpringExtension.class) when #DataJpaTest is used.
#DataJpaTest will allow to test spring data repository layer but will also guarantee spring dependency injection.
That is to say, and roughly speeking, you can use #Autowired annotation for a class which is annotated #DataJpaTest (a spring data repository layer).
From imports junit.jupiter i can see you are using junit-5, were #RunWith belongs to junit-4, and for reference #ExtendWith is not mandataroy for junit-5 test and if you want to use a specific runner you still require the #ExtendsWith but as #DataJpaTest itself is annotated with #ExtendsWith you don't need to do this explicitly
In JUnit 5, the #RunWith annotation has been replaced by the more powerful #ExtendWith annotation.
To use this class, simply annotate a JUnit 4 based test class with #RunWith(SpringJUnit4ClassRunner.class) or #RunWith(SpringRunner.class).

Could not autowire JobLauncherTestUtils

I am attempting to test a simple spring batch application.
Using the Spring Batch documentation as a guide (found here), I have created the following test class:
import org.junit.runner.RunWith;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.batch.test.context.SpringBatchTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.jupiter.api.Assertions.assertNotNull;
#SpringBatchTest
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = BatchConfig.class)
class BatchConfigTest {
#Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
#Test
void userStep() {
assertNotNull(jobLauncherTestUtils, "jobLauncherTestUtils should not be null");
}
}
According to docs #SpringBatchTest should inject the JobLaucherTestUtils bean. However, when I run the test, the assertion fails. I have also tried defining the bean in an inner configuration class and had the same result:
static class TestConfiguration {
#Autowired
#Qualifier("userJob")
private Job userJob;
#Bean
public JobLauncherTestUtils jobLauncherTestUtils() {
JobLauncherTestUtils utils = new JobLauncherTestUtils();
utils.setJob(userJob);
return utils;
}
}
Is there something I'm missing? The full source code can be found here.
I am using Spring Batch v4.2.0 and JUnit 5
You are using #RunWith(SpringRunner.class) which is for JUnit 4. You need to use #ExtendWith(SpringExtension.class) for JUnit 5 tests:
#SpringBatchTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = BatchConfig.class)
class BatchConfigTest {
// ...
}
I had a same problem with Spring Batch 4.1.3 and JUnit 4.12.
Replacing #SpringBatchTest with #SpringBootTest solved the problem.
I was seeing the same error in IntelliJ and spent ages looking into why before I ran the test. It was being injected just fine, IntelliJ was incorrectly telling me the bean wasn't there.
:facepalm:
Posting this in case someone faces the same issue.

Spring Unit Test and object re-injection

In Spring what is the best way to run a bunch of test methods on an object but reset/re-inject the object before each new test method invocation?
I have tried to do in the code below but with my current logic the object gets created and injected only once..
package com.bidtracker;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.is;
import com.bidtracker.iface.BidTracker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class BidTrackerTest {
#Autowired
BidTracker tracker;
#Test
public void shouldReturnHighestBidAmount1(){
tracker.bidOnItem("itemB", "user1", 105);
assertThat(tracker.getHighestBid("itemB").getAmount(),is(Integer.valueOf(105)));
}
#Test
public void shouldReturnHighestBidAmount2(){
tracker.bidOnItem("itemB", "user1", 39);
tracker.bidOnItem("itemB", "user1", 50);
assertThat(tracker.getHighestBid("itemB").getAmount(),is(Integer.valueOf(50)));
}
}
The issue here is that Spring test support by default caches the application context and will reuse the cached context if elements of the key match up(context name(s), active profiles etc). To ask Spring to remove the context from the cache you can mark the test with #DirtiesContext annotation (at the method level or test class level).
JUnit has the #Before annotation, you can use that to do any initialization before each test.
However, I am curious about your case. Here's from the #Test documentation:
The Test annotation tells JUnit that the public void method to which
it is attached can be run as a test case. To run the method, JUnit
first constructs a fresh instance of the class then invokes the
annotated method. Any exceptions thrown by the test will be reported
by JUnit as a failure. If no exceptions are thrown, the test is
assumed to have succeeded.
I'm guessing that the issue is that the bean is a singleton. It actually gets injected again, but if it's been modified you're using the same thing. You can try the #DirtiesContext annotation.

Resources