Is there any way to test an anonymous inner class using junit? - java

I have the following class that I would like to test. Having to test an anonymous inner class is proving to be very difficult. Any help would be appreciated.
#Configuration
public class CustomErrorConfiguration {
#Bean
public ErrorAttributes errorAttributes() {
return new DefaultErrorAttributes() {
#Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
errorAttributes.remove("timestamp");
errorAttributes.remove("status");
errorAttributes.remove("exception");
errorAttributes.remove("path");
if (errorAttributes.containsKey("error") && errorAttributes.containsKey("message")) {
Map<String, Object> attr = new HashMap<>();
attr.put("message", errorAttributes.get("message"));
return attr;
}
return errorAttributes;
}
};
}
}

This should be the minimum needed to test your inner class:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#ContextConfiguration
public class BeanTest {
#Autowired
ErrorAttributes errorAttributes;
#Test
public void testMyBean() {
RequestAttributes requestAttributes = new RequestAttributes();
System.out.println(errorAttributes.getErrorAttributes(requestAttributes, true));
}
#Configuration
#ComponentScan(basePackages = "package.where.your.configuration.is")
public static class SpringTestConfig {
}
}
This test does a few things:
It leverages SpringRunner.class to create an ApplicationContext for you test.
The #ContextConfiguration annotation picks up the the static nested class SpringTestConfig. This class does the heavy lifting and actually scans base packages looking for other classes marked with Configuration, Bean, Component, etc. This will discover your Configuration which will in turn cause instantiation of your Bean.
Since the application context is now setup we can inject the bean with #Autowired as you would in normal application code.
I needed the following maven dependencies to accomplish this (junit >= 4.12 required). All are available in maven central:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.0.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

Related

How to find a Bean instance of MongoRepository?

I am trying to use a MongoRepository, but Spring complains, that no instance of the MonoRepository-Interface is in the context.
#SpringBootApplication
public class BackendApplication {
public static void main(String[] args) {
SpringApplication.run(BackendApplication.class, args);
}
}
#Document(collection = "tableItems")
class TableItem {
#Id
public String id;
public int roundNbr;
}
interface TableItemsRepository extends MongoRepository<TableItem, String> {
}
#Service
class TableItemsService {
#Autowired
public TableItemsService(TableItemsRepository tableItemsRepository) {
}
}
The server complains:
Parameter 0 of constructor in backend.TableItemsService required a bean of type 'backend.TableItemsRepository' that could not be found.
Action:
Consider defining a bean of type 'backend.TableItemsRepository' in your configuration.
my maven pom.xml:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>3.4.0</version>
</dependency>
How can I get a bean instance of my MongoRepository?
The dependency is incorrect.
spring-data-mongodb only makes it compile:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>3.4.0</version>
</dependency>
But you need additional dependencies, to make it run. For convenience you can import spring-boot-starter-data-mongodb, which will import all required artifacts as transitive dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

How to raise a ConstraintValidationException in a test case for bean properties with validation annotations?

I'm trying to test that my beans have correct validation annotations. I'm using spring-boot. Here is an example test case:
package com.example.sandbox;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.validation.annotation.Validated;
#SpringBootTest
class ValidationTest {
#Test
void testConstructor() {
TestedBean bean = new TestedBean(null);
assertThatThrownBy(() -> checkIfValidated(bean)).isInstanceOf(ConstraintViolationException.class);
}
#Test
void testSetter() {
TestedBean bean = new TestedBean(null);
assertThatThrownBy(() -> bean.setSomeProperty(null)).isInstanceOf(ConstraintViolationException.class);
}
private void checkIfValidated(#Valid TestedBean bean) {
}
#Validated
class TestedBean {
#NotNull
private String someProperty;
public TestedBean(String someProperty) {
super();
this.someProperty = someProperty;
}
public String getSomeProperty() {
return someProperty;
}
public void setSomeProperty(#NotNull String someProperty) {
this.someProperty = someProperty;
}
}
}
I expect the call to checkIfvalidated() and to setSomeProperty(null) to raise a ConstraintViolationException, and the tests to pass, but they both fail with:
java.lang.AssertionError:
Expecting code to raise a throwable.
at com.example.sandbox.ValidationTest.test(ValidationTest.java:20)
...
My pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>com.example.springbootsandbox</artifactId>
<version>0.0</version>
<name>SpringBootSandbox</name>
<description>Sandbox for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Why is there no ConstraintViolationException raised here? The bean property has a #NotNull annotation, the bean itself is #Validated and the method signature requires a #Valid bean.
Is there a simple way to have that exception raised in the context of my test class?
When I use validation annotations on method signatures for a service interface, everything works as expected. I don't understand where is the difference.
Service interface:
package com.example.sandbox;
import javax.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
#Validated
public interface IService {
public void setValue(#NotNull String value);
}
Service implementation:
package com.example.sandbox;
import org.springframework.stereotype.Service;
#Service
public class SomeService implements IService {
#Override
public void setValue(String value) {
// Do nothing
}
}
Test case:
package com.example.sandbox;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import javax.validation.ConstraintViolationException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
class SomeServiceTests {
#Autowired
IService service;
#Test
void testSetValue() {
assertThatThrownBy(() -> service.setValue(null)).isInstanceOf(ConstraintViolationException.class);
}
}
==> The test passes.
Working code according to the given answer:
The test class:
#SpringBootTest
class ValidationTest {
#Autowired
private Validator validator; // Using the default validator to test property annotations
#Autowired
private TestedBeanService service; // Using a service to test method annotations
#Test
void testPropertyAnnotations() {
TestedBean bean = new TestedBean(null);
Set<ConstraintViolation<TestedBean>> violations = validator.validate(bean);
assertThat(violations).isNotEmpty();
}
#Test
void testMethodAnnotations() {
TestedBean bean = new TestedBean(null);
assertThatThrownBy(() -> service.setBeanProperty(bean, null)).isInstanceOf(ConstraintViolationException.class);
}
}
The tested bean:
#Validated
class TestedBean {
#NotNull
private String someProperty;
public TestedBean(String someProperty) {
super();
this.someProperty = someProperty;
}
public String getSomeProperty() {
return someProperty;
}
public void setSomeProperty(String someProperty) { // No more annotation on setter
this.someProperty = someProperty;
}
}
The service interface:
#Validated
public interface TestedBeanService {
// method annotation on the interface method
void setBeanProperty(TestedBean bean, #NotNull String someProperty);
}
The service implementation:
#Service
public class TestedBeanServiceImpl implements TestedBeanService {
#Override
public void setBeanProperty(TestedBean bean, String someProperty) {
bean.setSomeProperty(someProperty);
}
}
Why is there no ConstraintViolationException raised here? The bean property has a #NotNull annotation, the bean itself is #Validated and the method signature requires a #Valid bean.
Annotations by themselves do not mean anything, they should be processed in some way. In this case the #Validated annotation is processed by Spring for its beans. The test is not a Spring bean, so the framework does not look at the annotations related to valdidation, hence no exception.
Even if the test were a Spring Bean, the approach may not work out of the box. See this question for details.
Is there a simple way to have that exception raised in the context of my test class?
Take a look at this question
When I use validation annotations on method signatures for a service interface, everything works as expected. I don't understand where is the difference.
This happens because the service is a Spring bean, but test is not. When a method on the service is invoked, it gets intercepted by MethodValidationInterceptor, which is not the case for test

Null pointer Exception when injecting a rest-assured #Steps into spring-boot Test

Am new to springboot and am trying to integrate Integration Tests using Rest-assured to test my Rest-Api.
Am getting NPE when injecting #Steps into SpringBoot Test.I'm introducing a step class to improve on re-usability code.This test runs well if the step method is in the IT-class.I tried #Component annotation but it didn't work
Step class
import net.thucydides.core.annotations.Step;
import org.apache.http.HttpStatus;
import static com.jayway.restassured.RestAssured.when;
public class StaffSteps {
protected static String BASE_STAFF_URL = "/api/v1a/staff/";
protected static Staff staff;
#Step
public StaffSteps getStaffMemberById(String id){
staff = when().get(BASE_STAFF_URL+id)
.then().assertThat()
.statusCode(HttpStatus.SC_OK)
.extract()
.as(Staff.class);
return this;
}
#Step
public Staff getStaff(){return staff;}
}
import net.thucydides.core.annotations.Steps;
import org.apache.http.HttpStatus;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Arrays;
import java.util.List;
import static com.jayway.restassured.RestAssured.when;
#RunWith(SpringJUnit4ClassRunner.class)
public class StaffControllerIT extends BaseTest {
#Steps
private StaffSteps staffSteps;
#Before
public void setUp(){
}
#Test
public void getStaffMemberById(){
String id ="ff8081817049a34e017049a379320000";
Staff staff = staffSteps.getStaffMemberById(id).getStaff();
System.err.println(staff);
}
When i run this test, staffSteps is null.
Here is my dependency i used
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-core</artifactId>
<version>1.9.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
Please let me know if you need more information on this. Thanks
Solution using SpringRunner:
Annotate the steps with #Bean and #StepScope, and as a result, this object will share its lifetime with StepExecution.
public class StaffStepsConfig {
protected static String BASE_STAFF_URL = "/api/v1a/staff/";
protected static Staff staff;
#Bean
#StepScope
public StaffSteps getStaffMemberById(String id){
staff = when().get(BASE_STAFF_URL+id)
.then().assertThat()
.statusCode(HttpStatus.SC_OK)
.extract()
.as(Staff.class);
return this;
}
#Bean
#StepScope
public Staff getStaff(){return staff;}
}
In the Test class, the spring-batch-test dependency provides a set of useful helper methods and listeners that can be used to configure the Spring Batch context during testing.
#RunWith(SpringRunner.class)
//#SpringBatchTest
//#SpringBootTest
#EnableAutoConfiguration
#ContextConfiguration(classes = { StaffStepsConfig.class })
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
public class StaffControllerIT extends BaseTest {
#Autowired
private StaffSteps staffSteps;
#Before
public void setUp(){
}
#Test
public void getStaffMemberById(){
String id ="ff8081817049a34e017049a379320000";
Staff staff = staffSteps.getStaffMemberById(id).getStaff();
System.err.println(staff);
}
}
Note: This runner recognizes #SpringBootTest . I think the problem is in the way the test outcomes are generated. The steps are not being read by Serenity. Spring will inject #Autowired classes, and #serenity will inject #steps classes. I assume this happens because serenity and spring are creating components in different contexts .
required dependencies in your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
<version>2.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.9.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<version>4.2.0.RELEASE</version>
<scope>test</scope>
</dependency>

Application Failed to Start Spring Boot

I have one framework using Spring Boot which contains a controller RestController class ,
#RequestMapping("/details")
#RestController
public class DataController {
private KafkaStreams kafkaStreams;
public DataController(KafkaStreams kafkaStreams) {
this.kafkaStreams = kafkaStreams;
}
#Autowired
DataService dataService;
#RequestMapping(value = "getAllDetails", method = RequestMethod.GET)
public boolean getAllDetails(KafkaStreams kafkaStreams) {
return ktableService.getAllDetails(kafkaStreams);
}
}
In my service implementation class I am using this kafkaStreams object to find the details for my different services.
Now I am using this framework as a dependency in my one of the other application where I have a runner class,
import org.apache.kafka.streams.KafkaStreams;
#Component
public class PipelineRunner {
private final StreamsBuilder streamsBuilder;
private final KafkaProperties kafkaProperties;
private final SerdesExt serdesExt;
#Autowired
public PipelineRunner(StreamsBuilder streamsBuilder, KafkaProperties kafkaProperties, SerdesExt serdesExt) {
this.streamsBuilder = streamsBuilder;
this.kafkaProperties = kafkaProperties;
this.serdesExt = serdesExt;
}
#PostConstruct
public void run() {
ReflectData.AllowNull.get().addStringable(Utf8.class);
ReflectData.get().addStringable(Utf8.class);
DataProcessor processor = new DataProcessor(streamsBuilder, kafkaProperties,
serdesExt);
start();
}
private void start() {
KafkaStreams kafkaStreams = new KafkaStreams(streamsBuilder.build(),
kafkaProperties.getKafkaStreamsProperties(serdesExt));
System.out.println("----Its is started----");
DataController controller = new DataController(kafkaStreams);
kafkaStreams.start();
}
}
In this class i am trying to create the object for DataController .
So when I am trying to run the application class,
#SpringBootApplication(scanBasePackages = { "framework package" })
#EnableConfigurationProperties(KafkaProperties.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I am getting this error,
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in frameworkpackage.controllers.DataController required a bean of type 'org.apache.kafka.streams.KafkaStreams' that could not be found.
Action:
Consider defining a bean of type 'org.apache.kafka.streams.KafkaStreams' in your configuration.
I am new to Spring Boot. So I might be doing something wrong here. If more info is needed I can provide.
UPDATE
My pom file,
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
</parent>
<properties>
<confluent.version>4.1.0</confluent.version>
<kafka.version>1.1.0</kafka.version>
<lombok.version>1.18.0</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.confluent</groupId>
<artifactId>kafka-avro-serializer</artifactId>
<version>${confluent.version}</version>
</dependency>
<dependency>
<groupId>io.confluent</groupId>
<artifactId>kafka-streams-avro-serde</artifactId>
<version>${confluent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
<version>${kafka.version}</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>${kafka.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-avro</artifactId>
<version>2.8.5</version>
</dependency>
</dependencies>
A problem is that you are trying to use a bean kafkaStreams in your class DataController, but there is no bean with this name in Spring context. You need to create it manually, so you can autowire it later.
In your case I would suggest to update PipelineRunner.java like this:
import javax.annotation.PostConstruct;
import org.apache.kafka.streams.KafkaStreams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
#Component
public class PipelineRunner
{
private final StreamsBuilder streamsBuilder;
private final KafkaProperties kafkaProperties;
private final SerdesExt serdesExt;
#Autowired
public PipelineRunner(StreamsBuilder streamsBuilder, KafkaProperties kafkaProperties, SerdesExt serdesExt)
{
this.streamsBuilder = streamsBuilder;
this.kafkaProperties = kafkaProperties;
this.serdesExt = serdesExt;
}
#PostConstruct
public void run()
{
ReflectData.AllowNull.get().addStringable(Utf8.class);
ReflectData.get().addStringable(Utf8.class);
DataProcessor processor = new DataProcessor(streamsBuilder, kafkaProperties,
serdesExt);
start();
}
#Bean
KafkaStreams kafkaStreams()
{
KafkaStreams kafkaStreams = new KafkaStreams(
streamsBuilder.build(),
kafkaProperties.getKafkaStreamsProperties(serdesExt));
System.out.println("----Its is started----");
kafkaStreams.start();
return kafkaStreams;
}
}
You dont need to create an instance of DataController by yourself, this will be done automatically by Spring.
More information about Spring approach to beans is available there

How do I unit test Spring Controller using annotations?

I am new to the concept of unit testing with Spring controllers. I'm following some examples I found online and trying to implement their testing strategy. This is my basic controller:
#Controller
public class GreetingController {
#RequestMapping("/greeting")
public String greeting(#RequestParam(value = "name2", required = false, defaultValue = "World2") String name2,
Model model) {
model.addAttribute("name", name2);
return "greeting";
}
}
This is my unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class ControllerGreetingTest {
private MockMvc mockMvc;
#Autowired
GreetingController greetingController;
#Test
public void shouldReturnSomething() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(status().isOk())
.andExpect(view().name("greeting"));
}
}
Seems pretty straight forward but I get the following error:
java.lang.IllegalStateException: Neither GenericXmlWebContextLoader
nor AnnotationConfigWebContextLoader was able to detect defaults, and
no ApplicationContextInitializers were declared for context
configuration [ContextConfigurationAttributes#1698539 declaringClass =
'com.practice.demo.ControllerGreetingTest', locations = '{}', classes
= '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass =
'org.springframework.test.context.ContextLoader']
I'm assuming I have to add a parameter to the #ContextConfiguration annotation but not sure what to include in there.
EDIT = This is what I have so far:
public class ControllerGreetingTest {
private MockMvc mockMvc;
#Before
public void setup(){
this.mockMvc = standaloneSetup(new GreetingController()).build();
}
#Test
public void shouldReturnDefaultString() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(status().isOk())
.andExpect(view().name("greetings"))
.andExpect(model().attribute("name","World2"));
}
}
It does the job but it doesn't use any of the Spring annotations like I tried to do before.. this approach is not good so trying to figure out why I keep gettings errors whenever I include the annotations in my test file.
My POM:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>1.5.7.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>hamcrest-core</artifactId>
<groupId>org.hamcrest</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.3.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.7.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
You should use spring-boot-starter-test dependency. It has almost everything for testing.
And for testing controller part of a spring application, you should use #WebMvcTest annotation for your test class. With this annotation spring will load context just for controller part. Plus you don't need to setup method if you use this annotation. You can simply autowire mockMvc. Your test class should be like this:
#RunWith(SpringRunner.class)
#WebMvcTest(GreetingController.class)
public class ControllerGreetingTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private SomeServiceClass someServiceClass;
#Test
public void shouldReturnDefaultString() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(status().isOk())
.andExpect(view().name("greetings"))
.andExpect(model().attribute("name","World2"));
}
}
Note: Your controller does not have any autowired fields. In cases that controller has some autowired objects like service or repository objects. you can simply mock them with annotation #MockBean as you can see above code.
See this link for other test slice annotations spring provided
For projects with org.springframework.boot:spring-boot-starter-test can use
#RunWith(SpringRunner.class)
#SpringBootTest(classes = App.class)
public class ControllerGreetingTest {
...
}
Where App.class is you main application class annotated with #SpringBootApplication. But you better read the documentation. And if you don't want to include (classes = App.class) part you also can change folder structure
For simple controllers it is possible to perform simple standalone tests
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = ControllerGreeting.class)
public class ControllerGreetingTest {
#Autowired
private MockMvc mockMvc;
...
}
Just add the #ContextConfiguration annotation and refer one or more XML configuration file locations or one or more configuration classes. Otherwise Spring cannot autowire your controller, which should be tested.
Example: You want to test a controller, which uses MyService via #Autowired:
MyControllerTest: Injects the controller, which should be tested using the MyTestConfig configuration class.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {MyTestConfig.class})
#WebAppConfiguration
public class MyControllerTest {
private MockMvc mockMvc;
#Autowired
private MyController controller;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
// Tests
}
MyTestConfig: Returns all beans, which are required for the test. Use Mockito to mock the depedencies of your controller, because we want to test only the controller and not the service layer.
#Configuration
public class MyTestConfig {
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
#Bean
public MyController myController() {
return new MyController();
}
}

Categories