#DeleteMapping(value = "/{id}", produces = APPLICATION_JSON_VALUE)
public ResponseEntity<Void> delete(#PathVariable Long id) {
log.debug("Delete by id Logo : {}", id);
try {
Logo entr = new Logo();
entr.setId(id);
logoRepository.delete(entr);
return ResponseEntity.ok().build();
} catch (Exception x) {
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}
}
For testing controllers we could mock mvc. Inject mock of service layers inside the controller.
Since you have mock for your service you could mock the response that service should return for a transaction using Mockito.when statement.
A sample code will be like.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = HelloController.class)
public class HelloWorldTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private HelloService service;
#Test
public void testShouldReturnMessage() throws Exception {
when(service.sayHello()).thenReturn("Hello World");
mockMvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.ALL))
.andExpect(MockMvcResultMatchers.status().is(200))
.andExpect(MockMvcResultMatchers.content().string("Hello World"))
.andExpect(MockMvcResultMatchers.header().string("Content-Type", "text/plain;charset=UTF-8"))
.andExpect(MockMvcResultMatchers.header().string("Content-Length", "11"));
}
}
Reference - https://www.nexsoftsys.com/articles/spring-boot-controller-unit-testing.html#:~:text=Unit%20testing%20Spring%20Boot%20controllers%20Technology%20Unit%20testing,is%20to%20validate%20each%20unit%20performs%20as%20designed.
Related
This question has already been asked. The accepted answer doesn't work for me. Here is my code:-
My service is here:
#Service
public class PlantService {
#Autowired
RestTemplate restTemplate;
static String url = "http://some_url_?Combined_Name=Oak";
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
public String getJson() {
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
return response.getBody();
}
}
My unit test
#RunWith(SpringRunner.class)
class PlantServiceTest {
private PlantService plantService;
#Mock
#Autowired
private RestTemplate restTemplate;
#Before
void setUp() {
MockitoAnnotations.initMocks(this);
plantService = new PlantService();
}
#Test
void testGetJsonString() {
// arrange
String expectedJson = "Some json string";
ResponseEntity mocResponse = mock(ResponseEntity.class);
// act
when(restTemplate.getForEntity("url", String.class)).thenReturn(mocResponse);
String actualJson = plantService.getJson();
// assert
assertSame(expectedJson, actualJson);
}
}
When I debug and step into the actual code. I can see restTemplate is null and throws java.lang.NullPointerException. So how do I unit test this code?
I have tried your code running on my machine.
Please find the test running test class
#RunWith(SpringRunner.class)
class PlantServiceTest {
#InjectMocks
private PlantService plantService;
#Mock
private RestTemplate restTemplate;
String url = "http://some_url_?Combined_Name=Oak";
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
void testGetJsonString() {
// arrange
String expectedJson = "Some json string";
ResponseEntity mocResponse = new ResponseEntity("Some json string", HttpStatus.OK);
// act
when(restTemplate.getForEntity(url, String.class)).thenReturn(mocResponse);
String actualJson = plantService.getJson();
// assert
assertSame(expectedJson, actualJson);
}
}
You can do the following:
Remove #RunWith annotation.
Annontate your test class with #RestClientTest from org.springframework.boot.test.autoconfigure.web.client.RestClientTest.
Use MockRestServiceServer from org.springframework.test.web.client.MockRestServiceServer.
Mock the response of the server when being called in the test method, example:
#RestClientTest
public class MyTest {
#Autowired
private MockRestServiceServer server;
public void test() {
// setup
String expected = "test_value";
server.expect(requestToUriTemplate("/myendpoint"))
.andRespond(withSuccess(myJsonResponse, MediaType.APPLICATION_JSON));
// act
String actual = myClient.fetch(myRequestDto);
// assert
assertThat(actual, equalTo(expected));
server.verify();
}
}
I'm using assertThat from hamcrest, you can use whatever you want to assert the correctness of the result.
You should use constructor injection in your PlantService. For instance:
public class PlantService {
RestTemplate restTemplate;
#Autowired
public PlantService(RestTemplate restTemplate){
this.restTemplate = restTemplate;
}
}
And on your test you can just do:
plantService = new PlantService(restTemplate);
^^
yourMock
Your problem is the plantService = new PlantService(); You never inject into this selft created instance.
Solution 1
I usually do it like this:
#InjectMocks
private PlantService plantService = new PlantService();
#Mock
private RestTemplate restTemplate;
Remove the setup method and run with Mockito instead of the SpringRunner.
Solution 2
If you need the SpringRunner you can do the following:
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
plantService = new PlantService();
ReflectionTestUtils.setField(plantService, "restTemplate", restTemplate);
}
As I've worked with JUnit 5 in the last years, I'm not sure about the SpringRunner. In JUnit 5 I can use both extensions (Spring and Mockito at the same time). Maybe this also worked in JUnit 4.
What is the best and easiest solution to test these sample get mappings? Could you show some easy example?
#GetMapping("/")
public List<UserDto> get() {
return userService.getUsers().stream().map((User user) -> toUserDto(user)).collect(Collectors.toList());
}
#GetMapping(path = "/{id}")
public HttpEntity<UserDto> findById(#PathVariable(name = "id") long id) {
User user = userService.unique(id);
if (user != null) {
return new ResponseEntity<>(toUserDto(user), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
Use MockMvc to test controller end points.
#RunWith(MockitoJUnitRunner.class)
public class UserControllerTest {
#InjectMock
private UserContoller controller;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(this.controller).build();
}
#Test
public void testFindById() {
// build your expected results here
String url = "/1";
MvcResult mvcResult = mockMvc
.perform(MockMvcRequestBuilders.get(url)
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
String responseAsJson = "some expected response";
Assert.assertEquals("response does not match", mvcResult.getResponse().getContentAsString(),
responseAsJson);
// verify the calls
}
}
EDIT : Adding link to my similar answer here for your reference Spring 5 with JUnit 5 + Mockito - Controller method returns null
I'm using SpringBoot 2 and Spring 5 (RC1) to expose reactive REST services. but I can't manage to write unit test for those controllers.
Here is my controller
#Api
#RestController
#RequestMapping("/")
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(path = "/", method = RequestMethod.GET)
public Flux<MyModel> getPages(#RequestParam(value = "id", required = false) String id,
#RequestParam(value = "name", required = false) String name) throws Exception {
return myService.getMyModels(id, name);
}
}
myService is calling a database so I would like not to call the real one. (I don't wan't integration testing)
Edit :
I found a way that could match my need but I can't make it work :
#Before
public void setup() {
client = WebTestClient.bindToController(MyController.class).build();
}
#Test
public void getPages() throws Exception {
client.get().uri("/").exchange().expectStatus().isOk();
}
But I'm getting 404, seems it can't find my controller
You have to pass actual controller instance to bindToController method.
As you want to test mock environment, you'll need to mock your dependencies, for example using Mockito.
public class MyControllerReactiveTest {
private WebTestClient client;
#Before
public void setup() {
client = WebTestClient
.bindToController(new MyController(new MyService()))
.build();
}
#Test
public void getPages() throws Exception {
client.get()
.uri("/")
.exchange()
.expectStatus().isOk();
}
}
More test examples you can find here.
Also, I suggest switching to constructor-based DI.
I really searched and followed the steps of creating a unit test class for spring MVC controller, however unit test is running with a green pass flag but the framework uses the original service class and it calls to the database. I mocked the class and used #InjectMocks together with MockitoAnnotations.initMocks(this). Still when the test runs, the controller uses original service object rather than the mocked object. I really appreciate if somebody can help me in this regards.
Here is UserManager(service class), UserRegisterController(controller), TestUserRegisterController (Test class) classes with a picture of the Eclipse package structure
Service :
#Service
public class UserManager {
protected Map<String, String> getAllCertificates() {
Map<String, String> allCertificates = new HashMap<String, String>();
//call to database
return allCertificates;
}
protected User getUser(int userId) {
//create session
User user = session.get(User.class, userId);
//close session
return user;
}
}
Controller :
#Controller
public class UserRegisterController {
#Autowired
private UserManager manager;
#InitBinder
public void initBinder(WebDataBinder binder) {
//do some work
}
#RequestMapping(value = "/user.html", method = RequestMethod.GET)
public ModelAndView getUser(#RequestParam(value="userId", defaultValue="-1") String userId) {
User user1;
user1 = this.manager.getUser(Integer.parseInt(userId));
if (user1 == null) {
user1 = new User();
}
ModelAndView view = new ModelAndView("User", "user1", user1);
view.addObject("allCertificatesMap", this.manager.getAllCertificates());
return view;
}
#ModelAttribute
public void setModelAttribute(Model model) {
model.addAttribute("PageHeader", "lable.pageHeader");
}
}
Test class :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("test-spring-dispatcher-servlet.xml")
#WebAppConfiguration
public class TestUserRegisterController {
#Mock
private UserManager userManager;
#InjectMocks
private UserRegisterController userRegisterController;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
// Process mock annotations
MockitoAnnotations.initMocks(this);
User user2 = new User();
user2.setUserId(10006);
user2.setUserName("Reza");
user2.setHobby("Quadcopter");
user2.setPhone("4032376295");
when(this.userManager.getUser(10006)).thenReturn(user2);
when(this.userManager.getAllCertificates()).thenReturn(new HashMap<String, String>());
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getUser() {
try {
this.mockMvc.perform(get("/user.html").param("userId", "10006"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("/WEB-INF/jsp/User.jsp"))
.andExpect(MockMvcResultMatchers.view().name("User"))
.andExpect(model().attributeExists("allCertificatesMap"));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Package hierarchy
Use #RunWith(MockitoJUnitRunner.class) to get the #InjectMocks and other annotations to work
I have the following simple controller to catch any unexpected exceptions:
#ControllerAdvice
public class ExceptionController {
#ExceptionHandler(Throwable.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public ResponseEntity handleException(Throwable ex) {
return ResponseEntityFactory.internalServerErrorResponse("Unexpected error has occurred.", ex);
}
}
I'm trying to write an integration test using Spring MVC Test framework. This is what I have so far:
#RunWith(MockitoJUnitRunner.class)
public class ExceptionControllerTest {
private MockMvc mockMvc;
#Mock
private StatusController statusController;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new ExceptionController(), statusController).build();
}
#Test
public void checkUnexpectedExceptionsAreCaughtAndStatusCode500IsReturnedInResponse() throws Exception {
when(statusController.checkHealth()).thenThrow(new RuntimeException("Unexpected Exception"));
mockMvc.perform(get("/api/status"))
.andDo(print())
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.error").value("Unexpected Exception"));
}
}
I register the ExceptionController and a mock StatusController in the Spring MVC infrastructure.
In the test method I setup an expectation to throw an exception from the StatusController.
The exception is being thrown, but the ExceptionController isn't dealing with it.
I want to be able to test that the ExceptionController gets exceptions and returns an appropriate response.
Any thoughts on why this doesn't work and how I should do this kind of test?
Thanks.
I just had the same issue and the following works for me:
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(statusController)
.setControllerAdvice(new ExceptionController())
.build();
}
This code will add ability to use your exceptions controlled advice.
#Before
public void setup() {
this.mockMvc = standaloneSetup(commandsController)
.setHandlerExceptionResolvers(withExceptionControllerAdvice())
.setMessageConverters(new MappingJackson2HttpMessageConverter()).build();
}
private ExceptionHandlerExceptionResolver withExceptionControllerAdvice() {
final ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
#Override
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(final HandlerMethod handlerMethod,
final Exception exception) {
Method method = new ExceptionHandlerMethodResolver(ExceptionController.class).resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(new ExceptionController(), method);
}
return super.getExceptionHandlerMethod(handlerMethod, exception);
}
};
exceptionResolver.afterPropertiesSet();
return exceptionResolver;
}
Since you are using stand alone setup test you need to provide exception handler manually.
mockMvc= MockMvcBuilders.standaloneSetup(adminCategoryController).setSingleView(view)
.setHandlerExceptionResolvers(getSimpleMappingExceptionResolver()).build();
I had same problem a few days back, you can see my problem and solution answered by myself here Spring MVC Controller Exception Test
Hoping my answer help you out
Use Spring MockMVC to emulate a servletContainer to a point where you can incorporate any request filtering or exception handling tests in your unit tests suite.
You can configure this setup with the following approach:
Given a custom RecordNotFound exception...
#ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Record not found") //
public class RecordNotFoundException extends RuntimeException {
private static final long serialVersionUID = 8857378116992711720L;
public RecordNotFoundException() {
super();
}
public RecordNotFoundException(String message) {
super(message);
}
}
... and a RecordNotFoundExceptionHandler
#Slf4j
#ControllerAdvice
public class BusinessExceptionHandler {
#ExceptionHandler(value = RecordNotFoundException.class)
public ResponseEntity<String> handleRecordNotFoundException(
RecordNotFoundException e,
WebRequest request) {
//Logs
LogError logging = new LogError("RecordNotFoundException",
HttpStatus.NOT_FOUND,
request.getDescription(true));
log.info(logging.toJson());
//Http error message
HttpErrorResponse response = new HttpErrorResponse(logging.getStatus(), e.getMessage());
return new ResponseEntity<>(response.toJson(),
HeaderFactory.getErrorHeaders(),
response.getStatus());
}
...
}
Configure a tailored test context: set a #ContextConfiguration to specify the classes you need for your test. Set Mockito MockMvc as a servlet container emulator and set your tests fixture and dependencies.
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {
WebConfig.class,
HeaderFactory.class,
})
#Slf4j
public class OrganisationCtrlTest {
private MockMvc mvc;
private Organisation coorg;
#MockBean
private OrganisationSvc service;
#InjectMocks
private OrganisationCtrl controller = new OrganisationCtrl();
//Constructor
public OrganisationCtrlTest() {
}
....
Configure a mock MVC "servlet emulator": register handler beans in the context and build the mockMvc emulator (Note: there are two possible configuration: standaloneSetup or webAppContextSetup; refer to the documentation). The builder rightfully implements the Builder pattern so you can chain configuration commands for exception resolvers and handlers before calling build().
#Before
public void setUp() {
final StaticApplicationContext appContext = new StaticApplicationContext();
appContext.registerBeanDefinition("BusinessExceptionHandler",
new RootBeanDefinition(BusinessExceptionHandler.class, null, null));
//InternalExceptionHandler extends ResponseEntityExceptionHandler to //handle Spring internally throwned exception
appContext.registerBeanDefinition("InternalExceptionHandler",
new RootBeanDefinition(InternalExceptionHandler.class, null,
null));
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(controller)
.setHandlerExceptionResolvers(getExceptionResolver(appContext))
.build();
coorg = OrganisationFixture.getFixture("orgID", "name", "webSiteUrl");
}
....
Get the exception resolver
private ExceptionHandlerExceptionResolver getExceptionResolver(
StaticApplicationContext context) {
ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
resolver.getMessageConverters().add(
new MappingJackson2HttpMessageConverter());
resolver.setApplicationContext(context);
resolver.afterPropertiesSet();
return resolver;
}
Run your tests
#Test
public void testGetSingleOrganisationRecordAnd404() throws Exception {
System.out.println("testGetSingleOrganisationRecordAndSuccess");
String request = "/orgs/{id}";
log.info("Request URL: " + request);
when(service.getOrganisation(anyString())).
thenReturn(coorg);
this.mvc.perform(get(request)
.accept("application/json")
.andExpect(content().contentType(
.APPLICATION_JSON))
.andExpect(status().notFound())
.andDo(print());
}
....
}
Hope this helps.
Jake.
Try it;
#RunWith(value = SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = { MVCConfig.class, CoreConfig.class,
PopulaterConfiguration.class })
public class ExceptionControllerTest {
private MockMvc mockMvc;
#Mock
private StatusController statusController;
#Autowired
private WebApplicationContext wac;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void checkUnexpectedExceptionsAreCaughtAndStatusCode500IsReturnedInResponse() throws Exception {
when(statusController.checkHealth()).thenThrow(new RuntimeException("Unexpected Exception"));
mockMvc.perform(get("/api/status"))
.andDo(print())
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.error").value("Unexpected Exception"));
}
}
This is better:
((HandlerExceptionResolverComposite) wac.getBean("handlerExceptionResolver")).getExceptionResolvers().get(0)
And do not forget to scan for #ControllerAdvice beans in your #Configuration class:
#ComponentScan(basePackages = {"com.company.exception"})
...tested on Spring 4.0.2.RELEASE