I am developing a JUnit test into a Spring Controller, the Controller calls a bunch of Services. My JUnit test code is above:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebApplicationConfiguration.class, DatabaseConfiguration.class, ServiceConfiguration.class})
#WebAppConfiguration
public class ProcessFileControllerTest {
private MockMvc mockMvc;
#Test
public void getPageTest() throws Exception{
final ProcessFileController controller = new ProcessFileController();
SecurityHelperUtility.setupSecurityContext("user", "pass", "ROLE_ADMIN");
mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get(URI.create("/processFile.html")).sessionAttr("freeTrialEmailAddress", "")).andExpect(view().name("processFile"));
}
When I run the JUnit test, I get a NullPointerExcelption when I call this particular line of code of my ProcessFileController.java
final Company company = companyService.getCompanyByUser(userName);
I can see that the Serviceis Null.
In my ProcessFileController.java the Service is being declared as:
#Autowired
private CompanyService companyService;
Any clue? Thanks in advance.
You are creating your controller using the new keyword. It should be a spring managed bean for spring to inject its dependencies. Use #Autowired
#Autowired
ProcessFileController controller;
Related
I'm writing service tests for my Spring Boot Web application that acts as an interface to MongoDB. Ideally, my service test will test each component of my Spring application before finally hitting a Mocked MongoTemplate. The following code uses a MockMvc to hit my web endpoints.
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
#AutoConfigureDataMongo
public class MyControllerServiceTest {
#Autowired
private MockMvc mvc;
#Autowired
private MongoTemplate mongoTemplate
#SpyBean
private MyMongoRepository myMongoRepository;
#Test
public void createTest() {
MyObject create = new MyObject()
given(this.myMongoRepository.insert(create));
this.mvc.perform(post("localhost:8080/myService")...)...;
}
}
MyController contains an #Autowired MyMongoRepository, which in turn implements MongoRepository and necessitates a mongoTemplate bean. This code executes properly only if a running MongoDB instance can be found (this example is more of an integration test between my service and MongoDB).
How can I mock out MongoTemplate while still using my MockMvc?
You need to add the following line to your test unit:
#MockBean
private MongoTemplate mongoTemplate;
For example, your class should look like this:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class, excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
public class MyMvcTests {
#Autowired
private MockMvc mvc;
#MockBean
private MyRepository repository;
#MockBean
private MongoTemplate mongoTemplate;
#Test
public void someTest() {}
}
You can find a complete Spring Boot application that include Integration and Unit tests here.
I think a better approach to test would be to test your web layer(controller) and your service layer separately.
For testing your web layer you can use MockMvc and you can mock your service layer.
For testing your service layer which in turn talks to mongo, you can use a Fongo and nosqlunit.
Some examples here
https://arthurportas.wordpress.com/2017/01/21/sample-project-using-spring-boot-and-mongodbfongo-and-test-repository-with-nosqlunit/
https://github.com/JohnathanMarkSmith/spring-fongo-demo
i have a #Service that I am trying to mock in an Unit Test but i get a null value so far. In the application class I specify what are the scanBasePackages. Do I have to do this in a different way? Thanks.
This is my service class that implements an interface:
#Service
public class DeviceService implements DeviceServiceDao {
private List<Device> devices;
#Override
public List<Device> getDevices(long homeId) {
return devices;
}
}
This is my unit test.
public class SmartHomeControllerTest {
private RestTemplate restTemplate = new RestTemplate();
private static final String BASE_URL = “..”;
#Mock
private DeviceService deviceService;
#Test
public void getHomeRegisteredDevices() throws Exception {
Device activeDevice = new DeviceBuilder()
.getActiveDevice(true)
.getName("Alexa")
.getDeviceId(1)
.getHomeId(1)
.build();
Device inativeDevice = new DeviceBuilder()
.getInactiveDevice(false)
.getName("Heater")
.getDeviceId(2)
.getHomeId(1)
.build();
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString(BASE_URL + "/1/devices");
List response = restTemplate.getForObject(builder.toUriString(), List.class);
verify(deviceService, times(1)).getDevices(1);
verifyNoMoreInteractions(deviceService);
}
You have to use a Spring test runner if you want to load and use a Spring context during tests execution.
You don't specify any runner, so it uses by default the runner of your test API. Here is probably JUnit or TestNG (the runner using depends on the #Test annotation specified).
Besides, according to the logic of your test, you want to invoke the "real"
REST service :
List response = restTemplate.getForObject(builder.toUriString(),
List.class);
To achieve it, you should load the Spring context and load the Spring Boot container by annotating the test with #SpringBootTest.
If you use a Spring Boot context, to mock the dependency in the Spring context, you must not use #Mock from Mockito but #MockBean from Spring Boot.
To understand the difference between the two, you may refer to this question.
Note that if you are using the #SpringBootTest annotation, a TestRestTemplate is automatically available and can be autowired into your test.
But beware, this is fault tolerant. It may be suitable or not according to your tests.
So your code could look like :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SmartHomeControllerTest {
private static final String BASE_URL = “..”;
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private DeviceService deviceService;
#Test
public void getHomeRegisteredDevices() throws Exception {
...
}
As a side note, avoid using raw type as List but favor generic type.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = NotificationApplication.class)
public class EmailClientImplTest {
...
}
And also add the needed properties/configs in
/src/test/resources/application.yml
Good luck!
I figured it out, I am using Mockito and used that to annotate my test class. This allowed me to get a mock of the service class that i am trying to use.
#RunWith(MockitoJUnitRunner.class)
public class SmartHomeControllerTest {..
#Mock
private DeviceService deviceService;
}
Try with #InjectMock instead of #Mock
You should run your test with spring boot runner
I am trying to get a unit test to work with Mockito and Spring MVC on a RESTful GET controller. Here is my test:
#RunWith(MockitoJUnitRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {"/test-context.xml","/dataaccess-context.xml"})
public class FormControllerTest {
private MockMvc mockMvc;
#Autowired
FormImplBean formBean;
#Mock
private FormService formServiceMock;
#InjectMocks
private FormController formController;
#Before
public void setup() {
// Process mock annotations
MockitoAnnotations.initMocks(this);
// Setup Spring test in standalone mode
this.mockMvc = MockMvcBuilders.standaloneSetup(formController).build();
}
#Test
public void testGet() throws Exception {
when(formServiceMock.getFormImplById(1)).thenReturn(formBean);
mockMvc.perform(get("/Form/form/{id}", 1))
.andExpect(status().is2xxSuccessful())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
verify(formServiceMock, times(1)).getFormImplById(1);
verifyZeroInteractions(formServiceMock);
}
}
And here is my controller method:
#RequestMapping(value = "/form/{formId}", method = RequestMethod.GET)
#ResponseBody
public FormImplBean getForm(#PathVariable("formId") int formId ) {
return formService.getFormImplById(formId);
}
I keep getting:
java.lang.AssertionError: Content type not set
Of course when I go an look at the real controller on the server, using firefox developer tools, I see that the content type is set correctly.
I tried adding the produces="application/json" to the controller but that did not work, (nor do I think I should have to right?)
Without the content type check, the test passes fine.
I am using:
Spring 4.2.7 -
Mockito 1.10.19 -
Jackson 2.7.0 -
Junit 4.12
in a maven build
Any Ideas?
I am expanding upon the basic Spring Boot examples, adding an "autowired" repository dependency to my controller. I would like to modify the unit tests to inject a Mockito mock for that dependency, but I am not sure how.
I was expecting that I could do something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MockServletContext.class)
#WebAppConfiguration
public class ExampleControllerTest {
private MockMvc mvc;
#InjectMocks
ExampleController exampleController;
#Mock
ExampleRepository mockExampleRepository;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(new ExampleController()).build();
}
#Test
public void getExamples_initially_shouldReturnEmptyList() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/example").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("[]")));
}
}
but it doesn't inject the mock into the MockMvc. Can anyone explain how to do this with #Autowired dependencies, rather than constructor arguments?
Please use #RunWith(MockitoJUnitRunner.class) instead of #RunWith(SpringJUnit4ClassRunner.class)
and you have to use the ExampleController exampleController; field with the injected mocks instead of creating a new one in line mvc = MockMvcBuilders.standaloneSetup(new ExampleController()).build();
I am trying to write a unit test for a REST controller which generates HATEOAS links via Resource assembler class. Everything is OK in production, but with the unit test Resource assembler class is not being injected into the controller.
my resource assembler class is:
#Component
public class ModelResourceAssembler extends ResourceAssemblerSupport<Model, ModelResource> {
public ModelResourceAssembler() {
super(ModelRestController.class, ModelResource.class);
}
#Bean
public ModelResourceAssembler modelResourceAssembler(){
return new ModelResourceAssembler();
}
#Override
public ModelResource toResource(Model model) {
...
}
}
The controller is:
#Controller
#RequestMapping("/demo")
#ComponentScan(basePackages = {"com.foo.demo"} )
public class ModelRestController {
#Autowired
private ModelPersistenceHandler modelPersistenceHandler;
#Autowired
private ModelResourceAssembler modelResourceAssembler;
...
}
And the unit test:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes= {ModelResourceAssembler.class, ModelRestController.class})
public class ModelRestControllerTest {
private MockMvc mockMvc;
#InjectMocks
private ModelRestController modelRestController;
#Mock
private ModelPersistenceHandler modelPersistenceHandler;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(modelRestController).build();
}
...
}
No matter what I do the ModelResourceAssembler instance is always null. Since the application is Spring Boot it does not have the WebCoonfig classes and autowired WebApplicationContext is always null, so I cannot (and really don't want to since I am running a unit test) instantiate MockMvc via webAppContextSetup
The solution ended up being quite simple: I needed to add one line to my test:
#Spy
private ModelResourceAssembler modelResourceAssembler;
And the bean was instantiated and properly wired
In your example you use #InjectMocks but don't declare a mock for ModelResourceAssembler. You don't get an instance out of nowhere.
You use the MockitoJUnitRunner.class. It has no idea of Spring beans. For testing Spring applications you rather want to use SpringJUnit4ClassRunner.class.
If i may suggest, if you use constructor injection for your controller then you can just mock the dependency and not need spring junit test runner stuff.