Is there any special configuration to use SpringRunner with junit5? [duplicate] - spring-boot

This question already has answers here:
Should SpringRunner be used in Spring Boot with Junit 5
(3 answers)
Closed last year.
My micro-service project based on spring-boot framework and all my unit test running with spring runner.
#RunWith(SpringRunner.class)
adding this annotations, imports the following library:
import org.springframework.test.context.junit4.SpringRunner;
How can I set my test classes to run with junit5 ?

Using JUnit Jupiter (aka JUnit 5) no longer requires ˋ #RunWith(SpringRunner.class)ˋ since this is a JUnit 4 mechanism. With recent versions of Spring/Spring Boot JUnit 5 support comes out of the box eg through using ˋspring-boot-starter-testˋ.
I recommend to exclude dependencies on JUnit 4 in your Maven/Gradle file to make confusing JUnit 4 and 5 features less likely.
Here’s an article that shows the basics: https://howtodoinjava.com/spring-boot2/testing/junit5-with-spring-boot2/

Remove JUnit4 from your build Path.
For example :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#TestPropertySource(locations = "classpath:application-local.properties")
public class MyTest {
#Before
public void setUp() {
...
}
#Test
public void testMethod() {
Assert.assertTrue(...);
}
}
will become
#SpringBootTest(classes = Application.class)
#TestPropertySource(locations = "classpath:application-local.properties")
public class MyTest {
#BeforeEach
public void setUp() {
...
}
#Test
public void testMethod() {
Assertions.assertTrue(...);
}
}

Spring 2.4 seems to include JUnit 5 and make it the default out of the box.
Besides updating #RunWith(SpringJUnit4ClassRunner.class) to #ExtendWith(SpringExtension.class) I had to add the following to build.gradle for the tests to actually run:
test {
useJUnitPlatform {}
}
This last step may have been due to JUnit 4 being a dependency of one of my dependencies, but every other thing I read didn't suggest this was needed.

The first annotation #RunWith(SpringRunner.class) is used to provide a bridge between Spring Boot test features and JUnit. SpringRunner.class enables full support of spring context loading and dependency injection of the beans in the tests. #SpringBootTest create ApplicationContext tests through SpringApplication that will be utilized in our tests. It bootstraps the entire container since the embedded server and creates a web environment.
In our test, we can mimic the real web environment setting it as RANDOM_PORT that also loads WebServerApplicationContext. The embedded server is started and listen to on a random port.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {YourPackage.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class YourClassTest {
#LocalServerPort
private int port;
#Autowired
TestRestTemplate restTemplate;
HttpHeaders headers = new HttpHeaders();
#ParameterizedTest
#JsonFileSource(resources = "/param.json")
void createBusinessEntity(JsonObject object){
....
}
}
#LocalServerPort annotation provides us the injected HTTP port that got allocated at runtime. It is a convenient alternative for #Value("${local.server.port}").
To access a third-party REST service inside a Spring application we use the Spring RestTemplate or TestRestTemplate the convenient alternative that is suitable for integration tests by injecting it in our test class. With spring-boot-starter-test dependency in our project, we can access to "TestRestTemplate" class in runtime.
In our test method, we are using the junit-json-params , a Junit 5 library that provides annotations to load data from JSON Strings or files in parameterized tests. We also annotated the method with #ParameterizedTest annotation to complement the library bellow. It is used to signal the annotated method is a parameterized test method. That method must not be private or static. They also must specify at least one ArgumentsProvider via #ArgumentsSource or a corresponding composed annotation.
Our #ArgumentsSource a JSON file #JsonFileSource(resources = "param.json") we put inside the test.resources package. #JsonFileSource lets you use JSON files from the classpath. It supports single objects, arrays of objects and JSON primitives.
The JSON object retrieved from the file is bound to the method params "object" that it is converted to a POJO object, in this case, our entity model.
In the Pom.xml we must import these libraries...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.joshka</groupId>
<artifactId>junit-json-params</artifactId>
<version>5.5.1-r0</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit-jupiter.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
Take a look at these articles that a post on DZone and my blog where you can access a complete sample and explanation step by step how to test spring boot microservice using Junit 5.
https://dzone.com/articles/microservices-in-publish-subscribe-communication-u
https://www.jeevora.com/2019/11/18/publish-subscribe-messaging-systems/

Related

Hystrix - behaviour of #HystrixCommand annotation

I have inherited a Java SpringBoot application (currently SpringBoot 2.7) that uses Hystrix for fault tolerance. I am aware that this has been deprecated, and we plan to more to Resilience4J
The Hystrix dependencies are
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.10.RELEASE</version>
<scope>compile</scope>
</dependency>
The main class in the application is annotated with:
#EnableCircuitBreaker
#EnableHystrixDashboard
#EnableHystrix
The following properties are specified:
hystrix.threadpool.default.coreSize=30
hystrix.threadpool.default.maxQueueSize=300
hystrix.threadpool.default.queueSizeRejectionThreshold=300
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=30000
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
hystrix.command.default.circuitBreaker.requestVolumeThreshold=60
hystrix.shareSecurityContext=true
I have gone through the usage of Hystrix within the application to understand exactly what the application is gaining from using Hystrix. I see that there are two different usages of the annotation HystrixCommand within the application.
In some cases, there are methods that are annotated as follows:
#HystrixCommand(fallbackMethod = "executeFallback")
public ResponseClass methodName (inputs...) {
private ResponseClass executeFallback(inputs...) {
LOGGER.error("Something went wrong...");
return new ResponseClass();
}
I have simplified the code, but my understanding is if the circuit is open, then it will result in the fallback method (named "executeFallback" above) being executed until the circuit is closed again.
But there are also many instances within the application of methods that are simply annotated with #HystrixCommand but there is no fallback method specified. These methods are also trying to access some data source, that could potentially be unavailable.
For example:
#HystrixCommand
public ResponseClass methodName (inputs...) {
My question is: what does an application gain from annotating methods with #HystrixCommand if no fallback method is specified?

Correct dependencies to use when using WebClient with Spring MVC

I'm using Spring MVC to develop some controllers.
I would like to write some scenario integration tests which will involve calling multiple controllers of my application.
Normally I would have just used RestTemplate within these tests but the documentation states:
Please, consider using the org.springframework.web.reactive.client.WebClient which has a more modern API and supports sync, async, and streaming scenarios.
And so I would like to write future-proof code by using the WebClient. But I have a number of questions:
What dependencies should be included here?
// (1) For Spring MVC:
'org.springframework.boot:spring-boot-starter-web:2.4.1'
// (2) For spring webflux
'org.springframework.boot:spring-boot-starter-webflux:2.4.1'
The problem is am I not including too much by having both (1) and (2)? Is there a separate dependency which I should include specifically just to get access to the WebClient? Or is it best practice to include both of these?
Should I write the tests using the WebClient or TestWebClient?
Given that I just want to make HTTP requests to my server in the integration tests I write - I think using the WebClient is fine? Or is it preferred to use the TestWebClient within these sort of tests? What is best practice?
Tested converting an existing integration test that was using TestRestTemplate into integration test using WebTestClient
package no.mycompany.myapp.user;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
#AutoConfigureWebTestClient
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class LoginControllerTest {
#Autowired
WebTestClient webTestClient;
#Test
public void postLogin_withoutUserCredentials_receiveUnauthorized() {
webTestClient.post()
.uri("/login")
.exchange()
.expectStatus().isUnauthorized();
}
}
Added this to POM
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<scope>test</scope>
</dependency>
The following was already in POM
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

Using a RestTemplate in a non Web app in SpringBoot app

I have a Spring Boot app that is not a web app, that has this piece of code
ResponseEntity<GeolocationAddress> response = new RestTemplate().getForEntity(urlStringConnection,
GeolocationAddress.class);
But then I have this error:
The import org.springframework.web cannot be
resolved
so I added this dependency
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.0.2.RELEASE</version>
</dependency>
But then when I start the app I got this error:
Could not evaluate condition on org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration due to org/springframework/web/context/support/StandardServletEnvironment not found. Make sure your own configuration does not rely on that class. This can also happen if you are #ComponentScanning a springframework package (e.g. if you put a #ComponentScan in the default package by mistake)
I may be a little late, but I had similar issues. By default Spring Boot tries to deduce the type of the application context to use by examining the class path. If it finds either javax.servlet.Servlet or org.springframework.web.context.ConfigurableWebApplicationContext, it instantiates a WebApplicationContext.
To avoid the error you got, I had to do the following in the main method of the app:
#SpringBootApplication
public class App {
public static void main(String[] args) {
new SpringApplicationBuilder(App.class)
.contextClass(AnnotationConfigApplicationContext.class).run(args);
}
}
I also excluded any unwanted AutoConfigure classes in the application.properties :
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration,org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration

Spring-boot not respecting liquibase properties

I'm in the process of setting up liquibase to manage my database in a new spring boot application. I need the liquibase dependency in my classpath to reset the database state after certain integration tests run. During my tests I do not want liquibase to be enabled via spring auto config during application context initialization. I've tried adding liquibase.enabled = false to the application.properties, however when I debug the LiquibaseAutoConfiguration class it appears that enabled is always set to true.
I'm not new to spring, but I am new to spring-boot's auto configuration. Has anyone had issues with spring boot not respecting properties in application.properties?
My setup is fairly minimal:
Relevant code snippets:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SpringBootClass.class })
public class databaseTests{
#Before
public void setup() throws LiquibaseException, SQLException {
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(dataSource.getConnection()));
Liquibase liquibase = new Liquibase("db/changelog/db.changelog-master.yaml", new FileSystemResourceAccessor("src/main/resources/"),database );
liquibase.dropAll();
liquibase.update("test");
}
..
}
#SpringBootApplication
#Import({ DataSourceConfig.class, HibernateConfig.class, OauthConfig.class })
#EnableConfigurationProperties
public class SpringBootClass {
..
}
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.6.RELEASE</version>
<!-- <liquibase.version>3.3.5</liquibase.version> -->
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<scope>test</scope>
</dependency>
If you want your tests to consume application.properties you need to run them as a Spring Boot application. Your use of #ContextConfiguration means that you're currently running them as a vanilla Spring Framework application. Replace the #ContextConfiguration annotation with #SpringApplicationConfiguration.
Should have RTFM...
from spring boot documentation
ConfigFileApplicationContextInitializer is an
ApplicationContextInitializer that can apply to your tests to load
Spring Boot application.properties files. You can use this when you
don’t need the full features provided by
#SpringApplicationConfiguration.
#ContextConfiguration(classes = Config.class,
initializers = ConfigFileApplicationContextInitializer.class)
Changing my config to use the initializer worked.

Spring-Boot module based integration testing

I have a multi-module Spring-Boot project.
I was wondering how I can set up integration testing just to test Spring Data JPA repositories? The following approach fails with this exception:
HV000183: Unable to load 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath.
Since this module does not depend on the web module, there is no web application that can be started.
#RunWith(SpringJUnit4ClassRunner.class)
#IntegrationTest
#SpringApplicationConfiguration(classes = TestConfiguration.class)
class CardInfoRepositoryIT {
#Autowired CardInfoRepository cardInfoRepository;
#Test
void testLoadData() {
assert cardInfoRepository.findAll().size() == 1
}
}
As Marten mentioned, #IntegrationTest should only be used when you need to test against the deployed Spring Boot application (e.g., deployed in an embedded Tomcat, Jetty, or Undertow container). So if your goal is to test your repository layer in isolation, you should not use #IntegrationTest.
On the other hand, if your tests require specific Spring Boot functionality (in contrast to standard Spring Framework functionality, semantics, and defaults), then you will in fact want to annotate your test class with #SpringApplicationConfiguration instead of #ContextConfiguration. The reason is that #SpringApplicationConfiguration preconfigures the SpringApplicationContextLoader which is specific to Spring Boot.
Furthermore, if you want your repository layer integration tests to run faster (i.e., without the full overhead of Spring Boot), you may choose to exclude configuration classes annotated with #EnableAutoConfiguration since that will auto-configure every candidate for auto-configuration found in the classpath. So, for example, if you just want to have Spring Boot auto-configure an embedded database and Spring Data JPA (with Hibernate as the JPA provider) along with entity scanning, you could compose your test configuration something like this:
#Configuration
#EnableJpaRepositories(basePackageClasses = UserRepository.class)
#EntityScan(basePackageClasses = User.class)
#Import({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
public class TestRepositoryConfig {}
And then use that configuration in your test class like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestRepositoryConfig.class)
#Transactional
public class UserRepositoryTests { /* ... */ }
Regards,
Sam
p.s. You might find my answer to the following, related question useful as well: Disable security for unit tests with spring boot
I resolved this by having the following test config class.
#Configuration
#EnableAutoConfiguration
#ComponentScan
#PropertySource("classpath:core.properties")
class TestConfiguration {
}
core.properties is also used by the main application and it contains datasource information. #IntegrationTest annotation can be removed on the test class.
I also added the following to the module as dependencies:
testRuntime 'javax.el:javax.el-api:2.2.4'
testRuntime 'org.glassfish.web:javax.el:2.2.4'

Resources