Mockito Test Controller - spring-mvc

This is my first attempt with Mockito.
My controller
#RequestMapping(value = "/add", method = RequestMethod.POST)
#ResponseBody
public ValidationResponse startVisitForPatient(PatientBO patientBO,Locale locale) {
ValidationResponse res = new ValidationResponse();
if (patientManagementService.startVisit(patientBO.getId())){
res.setStatus(MessageStatus.SUCCESS);
res.setValue(messageSource.getMessage("success.message", null, locale));
}
else{
res.setValue(messageSource.getMessage("failed.message", null, locale));
res.setStatus(MessageStatus.FAILED);
}
return res;
}
The service
#Transactional
public boolean startVisit(long id) {
Patient patient = patientRepository.findOne(id);
Set<Encounter> encounters = patient.getEncounters();
Encounter lastEncounter = null;
Timestamp startVisitDate = null;
Timestamp endVisitDate = null;
if (encounters.iterator().hasNext()){
lastEncounter = encounters.iterator().next();
startVisitDate = lastEncounter.getStartVisitDate();
endVisitDate = lastEncounter.getEndVisitDate();
}
if (lastEncounter == null || (endVisitDate != null && endVisitDate.after(startVisitDate))){
Encounter newEncounter = new Encounter();
newEncounter.setCreatedBy(userService.getLoggedUserName());
newEncounter.setCreatedDate(new Timestamp(new Date().getTime()));
newEncounter.setModifiedBy(userService.getLoggedUserName());
newEncounter.setModifiedDate(newEncounter.getCreatedDate());
newEncounter.setPatient(patient);
newEncounter.setStartVisitDate(newEncounter.getCreatedDate());
encounters.add(newEncounter);
return true;
}
else
return false;
}
Unit test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "file:src/main/webapp/WEB-INF/root-context.xml",
"file:src/main/webapp/WEB-INF/applicationContext.xml",
"file:src/main/webapp/WEB-INF/securityContext.xml" })
#WebAppConfiguration
public class Testing {
#InjectMocks
StaffVisitManagementController staffVisitManagementController;
#Mock
PatientManagementService patientManagementService;
#Mock
View mockView;
MockMvc mockMvc;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(staffVisitManagementController)
.setSingleView(mockView)
.build();
}
#Test
public void testStartVisit() throws Exception {
mockMvc.perform(post("/staff/visit/add").param("id", "1"))
.andExpect(status().isOk()).andExpect(content().string("success"));
}
}
The test method indeed calls the controller. However I am not able to debug the service at this line
patientManagementService.startVisit(patientBO.getId()))
All it returns is just false.
What am I missing here ?

When you mock something with Mockito, it mocks out everything to return some sort of default. For objects, this is null. For integers/doubles/etc, this is 0, for booleans, false. See the Mockito Docs. So you can't step into it because it's not your class that's present in the controller under test, it's a generated proxy that is merely pretending to be your class (hence, mocking).
If you want to change the behaviour of your class, you will need to use Mockito to tell it to return different variables depending on what is passed to the method, or which test it's running in. e.g.
when(patientManagementService.startVisit(1)).thenReturn(true);
Would mean that, if any code using the mocked PatientManagementService calls patientManagementService.startVisit(patientBO.getId()) where patientBO.getId() returns 1, then it will return true, otherwise it will return false, which is the default answer.
In your case, I suspect you would be better off mocking out patientRepository, rather than patientManagementService if you want to be able to step into your service-layer code.
EDIT:
Roughly what I would suggest is:
private StaffVisitManagementController staffVisitManagementController;
private PatientManagementService patientManagementService;
#Mock
private PatientRepository patientRepository;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(patientRepository.findOne(1)).thenReturn(new Patient());
patientManagementService = new PatientManagementService(patientRepository);
staffVisitManagementController = new StaffVisitManagementController(patientManagementService);
mockMvc = MockMvcBuilders.standaloneSetup(staffVisitManagementController)
.setSingleView(mockView)
.build();
}
Obviously, the name of repository class may be different, and you may be using field inject instead of constructor injection, etc, etc, but otherwise this should allow you to step into the PatientManagementService with the debugger. You will not be able to step into the PatientRepository, as that will be mocked.

Related

How to convert to Xunit using mocking

I have these two methods in my service class
public class PatientService : IPatientService
{
private readonly IRestClient _restClient;
private readonly IAppSettings _appSettings;
public PatientService(IRestClient restClient, IAppSettings appSettings)
{
_restClient = restClient;
_appSettings = appSettings;
}
public async Task<IList<PatientViewModel>> GetPatients(int wardId)
{
var url = _appSettings.Server + _appSettings.PatientServiceEndPoint + wardId;
var token = _appSettings.Token;
return GetPatientList(await _restClient.GetAsync<List<PatientInfo>>(url, token));
}
public IList<PatientViewModel> GetPatientList(IList<PatientInfo> patientInfoList)
{
return patientInfoList.Select(p => new PatientViewModel(p)).ToList();
}
}
I need to add this code to my Xunit.cs. How to do it?
I've implemented this and I do not know how to proceed.
private readonly PatientListPageViewModel _patientListPageViewModel;
private readonly Mock<IPatientService> _patient;
public PatientServiceTests()
{
_patient = new Mock<IPatientService>();
_patientListPageViewModel = new PatientListPageViewModel(_patient.Object);
}
[Fact]
public void GetListByWard_PassingWardId_GetPatientsCountAccordingToWardId()
{
}
This is what I tried to do. How to convert those two methods in service to be testable?
You did get mocking a bit wrong. It is not the component under test that is mocked, but its dependencies. When unit-testing you'd like to test a unit in isolation. Your case of mocking would be kind of correct if you unit-tested the PatientListPageViewModel, but since your test class is named PatientServiceTests I assume that you really wanted to test PatientService. If you wanted to test the former, you would be quite right to mock IPatientService, but when testing PatientService, IRestClient and IAppSettings shall be mocked
public PatientServiceTests()
{
_restClientMock = new Mock<IRestClient>();
_appSettingsMock = new Mock<IAppSettings>();
_patientService = new PatientService(_restClientMock.Object, _appSettingsMock.Object);
}
And your test could be something like
[Fact]
public async Task ReturnsCorrectPatientList() // async supported as of xUnit 1.9
{
// set up the mock
_restClientMock.SetUp(restClient => restClient.GetAsync<List<Patient>>(It.IsAny<string>(), It.IsAny<string>())
.Returns(() => Task.FromResult(/* what patients it shall return */));
var result = await _patientService.GetPatients(0);
// compare whether the returned result matches your expectations
}
If you wanted to test whether the URL is formed correctly, you could use Verify
[Theory]
[InlineData("SERVER", "ENDPOINT", 12, "1234", "SERVERENDPOINT12")]
[InlineData("https://localhost:65000", "/patients/", 5, https://localhost:65000/patients/5")]
public void TestWhetherCorrectUrlIsCalled(string server, string endpoint, int wardId, string token, string expectedUrl)
{
_appSettingsMock.SetupGet(appSettings => appSettings.Server).Returns(server);
_appSettingsMock.SetupGet(appSettings => appSettings.PatientServiceEndPoint).Returns(endpoint);
_appSettingsMock.SetupGet(appSettings => appSettings.Token).Returns(token);
_restClientMock.SetUp(restClient => restClient.GetAsync<List<Patient>>(It.IsAny<string>(), It.IsAny<string>())
.Returns(() => Task.FromResult(new List<Patient>()));
// we do not need the result
await _patientService.GetPatients(wardId);
_restClientMock.Verify(restClient => restClient.GetAsync<List<Patient>>(exptectedUrl, token), Times.Once);
}
We are setting up the IRestClient in this case, since it would return null otherwise. And await null would cause your test to fail. After GetPatients has been called we are using Verify to verify that GetAsync has been called with the correct parameters. If it has not been called, Verify will throw and your test will fail. Times.Once means, that GetAsync shall have been called once and only once.
On a side note: Viewmodels shall have a meaning in the context of your user interface only. Services shall be independent and hence not return viewmodels, as you did, but POCOs (or maybe domain models). In this case the interface of your service should be
public interface IPatientService
{
public async Task<IList<Patient>> GetPatients(int wardId);
// ...
}

Spring Boot MVC Test 404 with Valid Request

I am using Spring Boot 2.0.6 and have set up a test for a controller: the method is as follows:
#Secured("ROLE_ADMIN")
#GetMapping(value = {"/maintainers/aircrafts/workorders/workitems/{wid}/parts"}, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
Response<Page<WorkItem>> getPagedParts(
#PathVariable("wid") Optional<Long> workItemId,
#PageableDefault(page = DEFAULT_PAGE_NUMBER, size = DEFAULT_PAGE_SIZE)
#SortDefault.SortDefaults({
#SortDefault(sort = "partName", direction = Sort.Direction.ASC),
#SortDefault(sort = "partSpecification", direction = Sort.Direction.ASC)
}) Pageable pageable) {
LOG.info("looking for work: {}", workItemId);
return Response.of(workItemService.findAllPartsForWorkItem(workItemId.get(), pageable));
}
As you can see, it is supposed to do paging and sorting, but it doesn't even get past the path:
The test that tests it is as follows:
#ActiveProfiles("embedded")
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#EnableConfigurationProperties
#EnableJpaRepositories({ "au.com.avmaint.api" })
#AutoConfigureMockMvc
public class WorkItemControllerPartsFunctionalTest {
private static final Logger LOG = LoggerFactory.getLogger(WorkItemControllerFunctionalTest.class);
private String adminJwtToken;
#Autowired
private WebApplicationContext context;
#Autowired
private MockMvc mvc;
#Autowired
private UserService userService;
#Autowired
private RoleService roleService;
#Autowired
private CasaService casaService;
#Autowired
private MaintainerService maintainerService;
#Autowired
private MaintenanceContractService maintenanceContractService;
#Autowired
private WorkSetService workSetService;
#Autowired
private WorkSetTemplateService workSetTemplateService;
#Autowired
private AircraftService aircraftService;
Maintainer franks;
MaintenanceContract contract;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
franks = MaintainerFixtures.createFranksMaintainer(maintainerService, maintenanceContractService, casaService);
adminJwtToken = UserAndRoleFixtures.adminToken(userService, roleService, franks);
contract = WorkItemFixtures.makeDetailedJobOnContract(franks, maintainerService, maintenanceContractService, workSetTemplateService, casaService, aircraftService);
}
#Test
public void findingWorkItemsWithoutParts() throws Exception {
Set<WorkSet> sets = contract.getWorkOrders().stream().findFirst().get().getWorkSets();
WorkSet hundredHourly = sets.stream().filter(s -> s.getName().equals("100 Hourly for PA-31")).findFirst().orElse(null);
WorkItem opening = hundredHourly.getWorkItems().stream().filter(wi -> wi.getTitle().equals("Opening the aircraft")).findFirst().orElse(null);
LOG.info("opening item: {}", opening);
LOG.info("HUNDRED: {}", hundredHourly);
mvc.perform(get("/maintainers/aircrafts/workorders/workitems/" + opening.getId() + "/parts")
.header(AUTHORIZATION_HEADER, "Bearer " + adminJwtToken))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.payload").isNotEmpty())
.andExpect(jsonPath("$.payload.content").isNotEmpty())
.andExpect(jsonPath("$.payload.pageable").isNotEmpty())
.andExpect(jsonPath("$.payload.last").value(false))
.andExpect(jsonPath("$.payload.totalPages").value(3)) // page count
.andExpect(jsonPath("$.payload.totalElements").value(9)) // total count
.andExpect(jsonPath("$.payload.size").value(4)) // elements per page
.andExpect(jsonPath("$.payload.numberOfElements").value(4)) // elements in page
.andExpect(jsonPath("$.payload.number").value(0)) // current page number
.andExpect(jsonPath("$.payload.content").isArray())
// oops, lets not check dates, they're created on the instant
.andExpect(jsonPath("$.payload.content[0].pos").value("1"))
.andExpect(jsonPath("$.payload.content[0].title").value("Opening the aircraft"))
.andExpect(jsonPath("$.payload.content[0].category").value("AIRFRAME"))
;
}
#After
public void tearDown() {
MaintainerFixtures.removeFranks(franks, maintainerService, aircraftService);
WorkItemFixtures.killJobs(workSetService, workSetTemplateService);
UserAndRoleFixtures.killAllUsers(userService, roleService);
}
}
As the project makes extensive use of JPA, there are annotations and a lot of data setup, but all of this has worked fine with other tests and there don't appear to be any problems with the data. In fact a peek at the JSON output for the work order that this method should be querying...
work order JSON
Basically has all the data correctly set up. The spring boot startup includes this line:
2018-11-12 06:32:17.362 INFO 83372 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/api/maintainers/aircrafts/workorders/workitems/{wid}/parts],methods=[GET],produces=[application/json]}" onto au.com.avmaint.api.common.Response<org.springframework.data.domain.Page<au.com.avmaint.api.aircraft.model.WorkItem>> au.com.avmaint.api.aircraft.WorkItemController.getPagedParts(java.util.Optional<java.lang.Long>,org.springframework.data.domain.Pageable)
So the path appears to be OK
and now to the .andDo(print()) output:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /maintainers/aircrafts/workorders/workitems/5/parts
Parameters = {}
Headers = {Authorization=[Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJmcmFua0BmcmFua3MuY29tIiwic2NvcGVzIjpbIlJPTEVfQURNSU4iLCJST0xFX0JBU0lDIl0sImV4cCI6MTU0MjgyODczOH0.QOTiyWG_pVL9qb8MDG-2c_nkTnsIzceUH-5vvtmpZhBcdro9HqVADojK0-c6B1sAOOYOcprpwg4-wrBF0PGweg]}
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = org.springframework.web.servlet.resource.ResourceHttpRequestHandler
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 404
Error message = null
Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
and the 404. So I guess I'm breaking something somewhere, I just can't see what it is, can anyone help with this?
Sorry everyone, the effect of tearing my hair out for ages, finally posting the question and then finding the problem moments later.
The issue was that I forgot to put /api as the prefix on the path in the test. This prefix is put on the top of every controller with:
#RestController
#RequestMapping("/api")
public class WorkItemController {
so, yeah: it works now

How to test common error ErrorController in spring-boot with junit

In a spring boot project, I'd like to test my ErrorController with Junit.
The code is as the following snippet.
#RestController
public class ApiErrorController implements ErrorController {
private static final Logger LOGGER = LoggerFactory.getLogger(ApiErrorController.class);
#Value("${server.error.path}")
private String errorPath;
#Override
public String getErrorPath() {
return this.errorPath;
}
#RequestMapping("/error")
public ResponseEntity<ErrorResult> error(HttpServletRequest request, HttpServletResponse response) {
String requestURI = (String) request.getAttribute("javax.servlet.forward.request_uri");
LOGGER.info("error handling start url = {}", requestURI);
String servletMessage = (String) request.getAttribute("javax.servlet.error.message");
Integer servletStatus = (Integer) request.getAttribute("javax.servlet.error.status_code");
String[] messages = new String[0];
if (!StringUtils.isNullOrEmpty(servletMessage)) {
messages = new String[] { servletMessage };
}
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
try {
if (servletStatus != null && servletStatus instanceof Integer) {
status = HttpStatus.valueOf(servletStatus);
}
} catch (Exception ex) { // test this exception
LOGGER.warn("http status not converted.{}", request.getAttribute("javax.servlet.error.status_code"), ex);
}
ErrorResult body = new ErrorResult();
body.setMessages(messages);
ResponseEntity<ErrorResult> responseResult = new ResponseEntity<>(body, status);
return responseResult;
}
}
When a business exception happened in my Controller(for example AbcController), then the program goes into the ExceptionControllerAdvice class.
If an exception happened in ExceptionControllerAdvice, then the program goes into the above ApiErrorController class.
Could someone tell me how to test the case that HttpStatus.valueOf(servletStatus) fail?
In addition, I want request.getAttribute("javax.servlet.error.message") return a non-empty string.
How to achieve what I'd like to test?
By the way, I don't want to only test the logic of error method. I'd like to use AbcController I mentioned to make the test. What I want is when a error happens in AbcController, then the error method in ApiErrorController can handle it successfully.
APPEND:
For example, ExceptionControllerAdvice will handle the business exception.
#ControllerAdvice(annotations = RestController.class)
public class ExceptionControllerAdvice {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionControllerAdvice.class);
#ExceptionHandler({ BusinessCloudException.class })
public ResponseEntity<ErrorResult> handleBlCloudException(HttpServletRequest request, HttpServletResponse response,
BlCloudException ex) {
HttpStatus status = ErrorUtils.toHttpStatus(ex.getType());
ErrorResult body = new ErrorResult();
body.setMessages(ex.getMessageArray());
ResponseEntity<ErrorResult> responseResult = new ResponseEntity<>(body, status);
return responseResult;
}
}
If there's an error happened in the handleBlCloudException method, then the program goes into ApiErrorController to handle this error.
How do the program produce the a specific servletStatus and javax.servlet.error.message? How to mock to do this?
First of all there is quite a lot going on in that error method. You might consider moving some of the logic to a specialized class / public methods.
Apart from that i would suggest using Mockito.
Fist of all create a method to encapsulate the HttpStatus retrieval:
HttpStatus getHttpStatusByServletStatus(Integer servletStatus){
return HttpStatus.valueOf(servletStatus);
}
and change your code to :
if (servletStatus != null && servletStatus instanceof Integer) {
status = getHttpStatusByServletStatus(servletStatus);
}
Then the test class:
public ApiErrorControllerTest{
#Spy
private ApiErrorController apiErrorController;
#Mock
HttpServletRequest requestMock;
#Mock
HttpServletResponse responseMock;
#Befire
public void init(){
MockitoAnnotations.initMocks(this);
}
#Test
public void test(){
// Arrange
HttpStatus expectedStatus = // expected status
String expectedErrorMessage = // ..
doReturn(expectedStatus).when(apiErrorController)
.getHttpStatusByServletStatus(Mockito.anyString());
when(requestMock.getAttribute("javax.servlet.error.message"))
.thenReturn(expectedErrorMessage);
// other setup..
// Act
apiErrorController.error(requestMock, responseMock);
// Assertions
}

Mockito verify post object has same values not same object reference

This is my test:
#Test
public void shouldProcessRegistration() throws Exception {
Spitter unsaved = new Spitter("Gustavo", "Diaz", "gdiaz", "gd123");
Spitter saved = new Spitter(24L, "Gustavo", "Diaz", "gdiaz", "gd123");
SpitterRepository spittlerRepository = Mockito.mock(SpitterRepository.class);
Mockito.when(spittlerRepository.save(unsaved)).thenReturn(saved);
SpitterController spittleController = new SpitterController(spittlerRepository);
MockMvc mockSpittleController = MockMvcBuilders.standaloneSetup(spittleController).build();
mockSpittleController.perform(MockMvcRequestBuilders.post("/spitter/register")
.param("firstName", "Gustavo")
.param("lastName", "Diaz")
.param("userName", "gdiaz")
.param("password", "gd123"))
.andExpect(MockMvcResultMatchers.redirectedUrl("/spitter/" + saved.getUserName()));
Mockito.verify(spittlerRepository, Mockito.atLeastOnce()).save(unsaved);
}
This is my controller:
#Controller
#RequestMapping(value = "spitter")
public class SpitterController {
SpitterRepository spitterRepository;
#Autowired
public SpitterController(SpitterRepository spittlerRepository) {
this.spitterRepository = spittlerRepository;
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String processRegistration(Spitter spitter){
spitterRepository.save(spitter);
return "redirect:/spitter/" + spitter.getUserName();
}
}
I want to verify that spitterRepository.save was called passing the same unsaved object I defined in the test. But i'm getting this exception:
Argument(s) are different! Wanted:
spitterRepository.save(
spittr.Spitter#3bd82cf5
);
-> at spitter.controllers.test.SpitterControllerTest.shouldProcessRegistration(SpitterControllerTest.java:48)
Actual invocation has different arguments:
spitterRepository.save(
spittr.Spitter#544fa968
);
Use an ArgumentCaptor to capture the value passed to save, and then assert on it.
ArgumentCaptor<Spitter> spitterArgument = ArgumentCaptor.forClass(Spitter.class);
verify(spittlerRepository, atLeastOnce()).save(spitterArgument.capture());
assertEquals("Gustavo", spitterArgument.getValue().getName());
For asserting if the Bean is the same, I would recommend you to use Hamcrest's samePropertyValues (http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/beans/SamePropertyValuesAs.html)
If I understand correctly, the below line initializes a mock where you can utilize the capabilities in Spring.
MockMvc mockSpittleController = MockMvcBuilders.standaloneSetup(spittleController).build();
When you call the mock like below, you will not call the method with the unsaved object. Rather, I guess a new object will be created.
mockSpittleController.perform(MockMvcRequestBuilders.post("/spitter/register")
.param("firstName", "Gustavo")
.param("lastName", "Diaz")
.param("userName", "gdiaz")
.param("password", "gd123"))
.andExpect(MockMvcResultMatchers.redirectedUrl("/spitter/" + saved.getUserName()));
This makes the verification fail, because the instances will not be the same.
To solve this, you should make sure that Spitter implements equals() and then use the eq() matcher for the verification:
Mockito.verify(spittlerRepository, Mockito.atLeastOnce()).save(org.mockito.Matchers.eq(unsaved));
This will check if the expected argument equals() what was passed.
import org.mockito.Matchers;
//...
Mockito.verify(spittlerRepository, Mockito.atLeastOnce()).save(Matchers.refEq(unsaved));

Moq: setting up for a method which returns no value

I'm trying to mock out a call to a repository. I can successfully do so when a repository call returns a value by using Setup().Returns:
mock.Setup(m => m.Get(param)).Returns(new CustomObject());
However, when I try to do the same sort of Setup for repository calls which return no values, Moq throws an exception and tells me this method was never called i.e.Expected invocation on the mock exactly 1 times, but was 0 times
Precisely, I'm doing this:
mock.Setup(m => m.UpdateRepository(param1)); // UpdateRepository returns no value
service.DoUpdate(param1);
mock.Verify(m => m.UpdateRepository(param1), Times.Exactly(1));
Note: DoUpdate method only calls repository.UpdateRepository(param1);
Am I not using Moq setup correctly in this instance? Is there a different way to Setup methods which return no value?
Thanks in advance!
You don't need to setup the call to UpdateRepository. Just verify it.
Given these types:
public interface IRepository
{
void UpdateRepository(string value);
}
public class Service
{
public Service(IRepository repository)
{
_repository = repository;
}
public void DoUpdate(string value)
{
_repository.UpdateRepository(value);
}
private IRepository _repository;
}
Your test method could be as follows:
const string param1 = "whatever";
var repoMock = new Mock<IRepository>();
var sut = new Service(repoMock.Object);
sut.DoUpdate(param1);
repoMock.Verify(x => x.UpdateRepository(param1), Times.Once());
Try using Verifiable:
mock.Setup(m => m.UpdateRepository(param1)).Verifiable();

Resources