unable to get the response entity from an api with mockmvc - spring-boot

I have an issue when I try to run my controller's unit test class. I get always a empty body in the response and I don't manage to find why.
I put here the code. Maybe someone with an external vision will be able to see the reason.
the controller:
#ResponseBody
#PostMapping(path = "/upload", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<Object> uploadFile(#RequestParam("file") MultipartFile multipartFileData, #RequestParam(name="jobId", required = false) String jobId) {
JobStatus result;
try {
result = this.fileService.uploadFileChunk(multipartFileData, 1, 1, jobId);
}catch (ExecutionException|InterruptedException|IOException ex){
Thread.currentThread().interrupt();
return new ResponseEntity<>(ex,HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>(result,HttpStatus.OK);
}
the unit test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes= FileUploadServiceRestController.class)
public class FileUploadServiceControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#MockBean
private FileUploadServiceImpl fileService;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Test
public void testUploadFile()
throws Exception {
MockMultipartFile file
= new MockMultipartFile(
"file",
"hello.txt",
MediaType.TEXT_PLAIN_VALUE,
"Hello, World!".getBytes()
);
JobStatus job = new JobStatus("uuid", ConstantUtil.JOB_STARTED);
when(fileService.uploadFileChunk(Mockito.any(MultipartFile.class),Mockito.eq(1),Mockito.eq(1),Mockito.isNull())).thenReturn(job);
mockMvc.perform(MockMvcRequestBuilders.multipart("/file/upload").file(file))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk());
}
}
and the object which will be transfered:
public class JobStatus implements Serializable {
private static final long serialVersionUID = -4405865740177389860L;
private String jobId;
private String status;
public JobStatus() {
}
public JobStatus(String jobId, String status) {
this.jobId = jobId;
this.status = status;
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
For information, this controller works well when I call it from the client. I can see that the mock is well returned when I put a breakpoint at the end of the controller, but the response body stay empty.
I add here the result of print if it could help:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /file/upload
Parameters = {}
Headers = [Content-Type:"multipart/form-data"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = org.iso.fileservice.controller.FileUploadServiceRestController
Method = org.iso.fileservice.controller.FileUploadServiceRestController#uploadFile(MultipartFile, String)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotWritableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 500
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
thanks, Mathieu

Just ran into this today.
I found the answer on Why MockMvc request retrieve empty responseBody while test succeed?
I just added My restController with #Autowired instead of #InjectMocks, after that I started to retrieve the Response Entity instead of a 500 status response

Related

WebFlux API-Layer Test returns 404

I'm trying to get started with Spring WebFlux with Spring Boot 3.0
I'm Building a Person API with an open api generator.
The Application runs and gives the expected results when it is tested manually.
But I'm not able to get the API layer unit tested.
This is my Test Class
#WebFluxTest(controllers = {PersonApiController.class})
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {PersonMapperImpl.class, H2PersonRepository.class, PersonRepository.class})
#DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class PersonRouterTest {
#MockBean
private PersonService personService;
#Autowired
private WebTestClient client;
#ParameterizedTest
#CsvSource({"1234, Max Mustermann", "5678, Erika Musterfrau"})
void retrieve_a_name(String id, String name) {
when(personService.getPersonDataByID(1234)).thenReturn(Mono.just(new PersonData(1234, "Max Mustermann")));
when(personService.getPersonDataByID(5678)).thenReturn(Mono.just(new PersonData(5678, "Erika Musterfrau")));
client.get()
.uri(uriBuilder -> uriBuilder
.path("/persons/{id}")
.build(id))
.accept(MediaType.ALL)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.expectBody()
.jsonPath("$.name").isEqualTo(name);
}
This is my Controller Class
#Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2022-12-
09T09:14:36.692713900+01:00[Europe/Vienna]")
#Controller
#RequestMapping("${openapi.openAPIDefinition.base-path:}")
public class PersonApiController implements PersonApi {
private final PersonApiDelegate delegate;
public PersonApiController(#Autowired(required = false) PersonApiDelegate delegate) {
this.delegate = Optional.ofNullable(delegate).orElse(new PersonApiDelegate() {});
}
#Override
public PersonApiDelegate getDelegate() {
return delegate;
}
}
The API interface:
#Tag(
name = "Person",
description = "the Person API"
)
public interface PersonApi {
default PersonApiDelegate getDelegate() {
return new PersonApiDelegate() {
};
}
#Operation(
operationId = "findPersonById",
summary = "Find Person by ID",
tags = {"Person"},
responses = {#ApiResponse(
responseCode = "200",
description = "successful operation",
content = {#Content(
mediaType = "application/json",
schema = #Schema(
implementation = PersonData.class
)
)}
)}
)
#RequestMapping(
method = {RequestMethod.GET},
value = {"/persons/{id}"},
produces = {"application/json"}
)
default Mono<ResponseEntity<PersonData>> findPersonById(#Parameter(name = "id",description = "Person ID",required = true) #PathVariable("id") Integer id, #Parameter(hidden = true) final ServerWebExchange exchange) {
return this.getDelegate().findPersonById(id, exchange);
}
#Operation(
operationId = "savePerson",
summary = "Creates a new Person",
tags = {"Person"},
responses = {#ApiResponse(
responseCode = "200",
description = "successful operatoin",
content = {#Content(
mediaType = "application/json",
schema = #Schema(
implementation = PersonData.class
)
)}
)}
)
#RequestMapping(
method = {RequestMethod.POST},
value = {"/persons"},
produces = {"application/json"},
consumes = {"application/json"}
)
default Mono<ResponseEntity<PersonData>> savePerson(#Parameter(name = "PersonData",description = "") #RequestBody(required = false) Mono<PersonData> personData, #Parameter(hidden = true) final ServerWebExchange exchange) {
return this.getDelegate().savePerson(personData, exchange);
}
}
and finally my delegate impl:
#Service
public class PersonDelegateImpl implements PersonApiDelegate {
public static final Mono<ResponseEntity<?>> RESPONSE_ENTITY_MONO = Mono.just(ResponseEntity.notFound().build());
private final PersonService service;
private final PersonMapper mapper;
public PersonDelegateImpl(PersonService service, PersonMapper mapper) {
this.service = service;
this.mapper = mapper;
}
public static <T> Mono<ResponseEntity<T>> toResponseEntity(Mono<T> mono) {
return mono.flatMap(t -> Mono.just(ResponseEntity.ok(t)))
.onErrorResume(t -> Mono.just(ResponseEntity.internalServerError().build()));
}
#Override
public Mono<ResponseEntity<PersonData>> findPersonById(Integer id, ServerWebExchange exchange) {
Mono<com.ebcont.talenttoolbackend.person.PersonData> personDataByID = service.getPersonDataByID(id);
return toResponseEntity(personDataByID.map(mapper::map));
}
#Override
public Mono<ResponseEntity<PersonData>> savePerson(Mono<PersonData> personData, ServerWebExchange exchange) {
return PersonApiDelegate.super.savePerson(personData, exchange);
If I run the test class I always get:
< 404 NOT_FOUND Not Found
< Content-Type: [application/json]
< Content-Length: [139]
{"timestamp":"2022-12-09T08:45:41.278+00:00","path":"/persons/1234","status":404,"error":"Not Found","message":null,"requestId":"4805b8b8"}
I have tried to change the Context Configuration but I did not get it to work.
I found the Problem, changing the Test Config to :
#WebFluxTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {PersonMapperImpl.class, H2PersonRepository.class, PersonRepository.class, PersonApiController.class, PersonDelegateImpl.class})
#DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
Solved my Problem.
The Controller bean was not recognized. I had to add PersonApiCrontroller and PersonDelegateImpl to the Context Config. i then removed the PersonApiController from the #WebFluxTest annotation.

Getting error 404 instead of 200 in unit test

This is my CurriculoControllerTest.java class
#SpringBootTest
#ExtendWith(SpringExtension.class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#DisplayName("Curriculo Controller Test")
#ActiveProfiles("local")
#AutoConfigureMockMvc
#Import(CurriculoController.class)
class CurriculoControllerTest {
private final String JSON_FORMAT = "application/json; charset=utf-8";
private final String BASE_PATH = "/curriculos";
#MockBean
private CurriculoServiceImpl curriculoService;
#SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
#Autowired
private MockMvc mockMvc;
public static CurriculoDTO createCurriculoInput() {
return CurriculoDTO.builder()
.id(UUID.randomUUID())
.dadosPessoais(DadosPessoaisDTO.builder()
.nome("joão")
.cargo("programador")
.email("joao#email.com")
.build())
.build();
}
CurriculoDTO novoCurriculo = CurriculoDTO.builder()
.id(UUID.randomUUID())
.dadosPessoais(DadosPessoaisDTO.builder()
.nome("Bruno")
.build())
.build();
CurriculoDTO curriculoExpected = CurriculoDTO.builder()
.id(UUID.randomUUID())
.dadosPessoais(DadosPessoaisDTO.builder()
.nome("Bruno")
.cargo("programador")
.email("joao#email.com")
.build())
.build();
#Test
#DisplayName("Deve retornar sucesso ao atualizar os dados pessoais do currículo")
public void deveRetornarSucessoAoAtualizarDadosPessoaisDoCurriculo() throws Exception {
var ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
var json = ow.writeValueAsString(curriculoExpected.getDadosPessoais());
doReturn(curriculoExpected).when(curriculoService)
.updateDadosPessoais(createCurriculoInput().getDadosPessoais(), novoCurriculo.getId());
mockMvc.perform(patch(BASE_PATH + "/dados-pessoais/" + createCurriculoInput().getId()).contentType(JSON_FORMAT).content(json))
.andExpect(status().isOk());
}
}
CurriculoController.java
#RestController
#RequestMapping("/curriculos")
public class CurriculoController {
private final DateTimeFormatter YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private final CurriculoServiceImpl service;
#Autowired
public CurriculoController(CurriculoServiceImpl service) {
this.service = service;
}
#PatchMapping("/dados-pessoais/{id}")
public ResponseEntity<CurriculoDTO> updateDadosPessoais(#RequestBody #Valid DadosPessoaisDTO dto,
#PathVariable UUID id) {
Optional<CurriculoDTO> curriculo = Optional.ofNullable(service.findById(id));
if (curriculo.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(service.updateDadosPessoais(dto, id), HttpStatus.OK);
}
CurriculoServiceImpl
public CurriculoDTO updateDadosPessoais(DadosPessoaisDTO dto, UUID id) {
Optional<Curriculo> optCurriculo = repository.findById(id)
.map(curriculo -> {
curriculo.setNome(Objects.nonNull(dto.getNome())
? dto.getNome() : curriculo.getNome());
curriculo.setCargo(Objects.nonNull(dto.getCargo())
? dto.getCargo() : curriculo.getCargo());
curriculo.setEmail(Objects.nonNull(dto.getEmail())
? dto.getEmail() : curriculo.getEmail());
curriculo.setSumario(Objects.nonNull(dto.getSumario())
? dto.getSumario() : curriculo.getSumario());
curriculo.setLinguagem(Objects.nonNull(dto.getLinguagem())
? dto.getLinguagem() : curriculo.getLinguagem());
return repository.save(curriculo);
});
CurriculoDTO curriculoDTO = converter.mapCurriculoToCurriculoDTO(optCurriculo.orElse(null));
curriculoDTO.setDadosPessoais(dto);
return curriculoDTO;
}
I've tried dozens of different ways, but I keep getting the 404 error, even though my URL is correct, could it be because the ID is not being found?
java.lang.AssertionError: Status expected:<200> but was:<404>
Expected :200
Actual :404
You mocked CurriculoServiceImpl but haven't stubbed service.findById(id) - you get an empty curriculo and return HttpStatus.NOT_FOUND.
As a side note - you seem to be testing only one controller mocking a service it depends on - you may want to consider #WebMvcTest instead of #SpringBootTest

Optional int parameter 'movieId' is present but cannot be translated into a null value

I am working on Maven multi module project so this might build error?
I am trying to perform unit test of delete method from controller and I quite don't understand why it is happening here because methods similar to this one works.
Exception speaks for itself - cannot convert int to null. But why there is null value where it looks like curl looks correct?
{"timestamp":"2019-12-16T11:47:35.450+0000","path":"/movie-composite/1","status":500,"error":"Internal Server Error","message":"Optional int parameter 'movieId' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type."}
Ho mapping looks like:
/**
* Sample usage: curl $HOST:$PORT/movie-composite/1
*
* #param movieId
*/
#ApiOperation(
value = "${api.movie-composite.delete-composite-movie.description}",
notes = "${api.movie-composite.delete-composite-movie.notes}")
#ApiResponses(value = {
#ApiResponse(code = 400, message = "Bad Request, invalid format of the request. See response message for more information."),
#ApiResponse(code = 422, message = "Unprocessable entity, input parameters caused the processing to fails. See response message for more information.")
})
#DeleteMapping(
value = "/movie-composite/{movieId}",
produces = "application/json")
void deleteCompositeMovie(#PathVariable int movieId);
its implementation:
#Override
public void deleteCompositeMovie(int movieId) {
log.debug("deleteCompositeMovie will delete Movie, Reviews, Recommendations belonging to Movie with id: {}", movieId);
movieCompositeIntegration.deleteMovie(movieId);
movieCompositeIntegration.deleteReviews(movieId);
movieCompositeIntegration.deleteRecommendations(movieId);
log.debug("deleteCompositeMovie deleted Movie, Reviews, Recommendations belonging to Movie with id: {}", movieId);
}
And finally test that won't pass:
#Test
void deleteCompositeMovie() {
int given = 1;
deleteAndVerify(given, HttpStatus.OK);
verify(baseMovieCompositeService, times(1)).deleteCompositeMovie(given);
}
where deleteAndVerify(given, HttpStatus.OK) looks like:
private void deleteAndVerify(int id, HttpStatus httpStatus) {
webTestClient.delete()
.uri("/movie-composite/" + id)
.exchange()
.expectStatus().isEqualTo(httpStatus);
}
complete test file looks like:
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = RANDOM_PORT)
public class MovieCompositeServiceApplicationTests {
public static final String FAKE_ADDRESS = "Fake address";
public static final String FAKE_GENRE = "Fake genre";
public static final String FAKE_TITLE = "Fake title";
#Autowired
WebTestClient webTestClient;
#MockBean
MovieCompositeIntegration movieCompositeIntegration;
#MockBean
BaseMovieCompositeService baseMovieCompositeService;
#MockBean
ServiceUtil serviceUtil;
#Test
void createMovie() {
int movieId = 1;
MovieAggregate movieAggregate = MovieAggregate.builder()
.movieId(movieId)
.genre(FAKE_GENRE)
.title(FAKE_TITLE)
.recommendations(getRecommendationSummaries())
.reviews(getReviewSummaries())
.serviceAddresses(null)
.build();
postAndVerify(movieAggregate);
}
#Test
void getMovieById() {
int given = 1;
Movie movie = getMovies(given);
Mockito.when(serviceUtil.getServiceAddress()).thenReturn("Fake service address");
List<Recommendation> recommendations = getRecommendations(movie);
List<Review> reviews = getReviews(movie);
Mockito.when(movieCompositeIntegration.getMovie(given)).thenReturn(movie);
Mockito.when(movieCompositeIntegration.getRecommendations(movie.getMovieId())).thenReturn(recommendations);
Mockito.when(movieCompositeIntegration.getReviews(movie.getMovieId())).thenReturn(reviews);
getAndVerifyMovie(given, HttpStatus.OK)
.jsonPath("$.movieId").isEqualTo(given)
.jsonPath("$.recommendations.length()").isEqualTo(3)
.jsonPath("$.reviews.length()").isEqualTo(3);
}
#Test
void getMovieByIdThrowsNotFoundException() {
int given = 1;
Mockito.when(movieCompositeIntegration.getMovie(given)).thenThrow(NotFoundException.class);
getAndVerifyMovie(given, HttpStatus.NOT_FOUND)
.jsonPath("$.path").isEqualTo("/movie-composite/" + given);
}
#Test
void getMovieByIdThrowsInvalidInputException() {
int given = 1;
Mockito.when(movieCompositeIntegration.getMovie(given)).thenThrow(InvalidInputException.class);
getAndVerifyMovie(given, HttpStatus.UNPROCESSABLE_ENTITY)
.jsonPath("$.path").isEqualTo("/movie-composite/" + given);
}
#Test
void deleteCompositeMovie() {
int given = 1;
deleteAndVerify(given, HttpStatus.OK);
verify(baseMovieCompositeService, times(1)).deleteCompositeMovie(given);
}
private WebTestClient.BodyContentSpec getAndVerifyMovie(int id, HttpStatus status) {
return webTestClient.get()
.uri("/movie-composite/" + id)
.accept(MediaType.APPLICATION_JSON_UTF8)
.exchange()
.expectStatus().isEqualTo(status)
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBody();
}
private WebTestClient.BodyContentSpec postAndVerify(MovieAggregate movieAggregate) {
return webTestClient.post()
.uri("/movie-composite")
.body(just(movieAggregate), MovieAggregate.class)
.exchange()
.expectStatus().isEqualTo(HttpStatus.OK)
.expectBody();
}
private void deleteAndVerify(int id, HttpStatus httpStatus) {
webTestClient.delete()
.uri("/movie-composite/" + id)
.exchange()
.expectStatus().isEqualTo(httpStatus);
}
private List<ReviewSummary> getReviewSummaries() {
return Collections.singletonList(ReviewSummary.builder().reviewId(1).subject("s").author("a").content("c").build());
}
private List<RecommendationSummary> getRecommendationSummaries() {
return Collections.singletonList(RecommendationSummary.builder().recommendationId(1).author("a").content("c").rate(1).build());
}
private Movie getMovies(int given) {
return Movie.builder().movieId(given).address(FAKE_ADDRESS).genre(FAKE_GENRE).title(FAKE_TITLE).build();
}
private List<Review> getReviews(Movie movie) {
return Arrays.asList(
Review.builder().movieId(movie.getMovieId()).reviewId(1).author("Author 1").subject("Subject 1").content("Content 1").serviceAddress(serviceUtil.getServiceAddress()).build(),
Review.builder().movieId(movie.getMovieId()).reviewId(2).author("Author 2").subject("Subject 2").content("Content 2").serviceAddress(serviceUtil.getServiceAddress()).build(),
Review.builder().movieId(movie.getMovieId()).reviewId(3).author("Author 2").subject("Subject 3").content("Content 3").serviceAddress(serviceUtil.getServiceAddress()).build()
);
}
private List<Recommendation> getRecommendations(Movie movie) {
return Arrays.asList(
Recommendation.builder().movieId(movie.getMovieId()).recommendationId(1).author("Author 1").rate(1).content("Content 1").serviceAddress(serviceUtil.getServiceAddress()).build(),
Recommendation.builder().movieId(movie.getMovieId()).recommendationId(2).author("Author 2").rate(2).content("Content 2").serviceAddress(serviceUtil.getServiceAddress()).build(),
Recommendation.builder().movieId(movie.getMovieId()).recommendationId(3).author("Author 3").rate(3).content("Content 3").serviceAddress(serviceUtil.getServiceAddress()).build()
);
}
}
Why it won't pass where getMovieById() looks very similar when it comes to input and url and it passes?

#WebMvcTest - How to configure security so the test can run?

I am trying to unit test my Spring Controller service that returns a String. I want to assert that the expected url name is correct but my test always returns a null url. I am using SpringRunner, #WebMvcTest and MockMvc for this.
#Slf4j
#Controller
public class CompanyInfoController {
private CompanyService companyService;
#Autowired
public CompanyInfoController(final CompanyService companyService) {
this.companyService = companyService;
}
#PreAuthorize("#someService.hasRole('" + Constants.MY_ROLE + "')")
#RequestMapping(value = "/companyInfo", method = RequestMethod.GET)
public String getCompanyInfo(final HttpServletRequest request, final
Model model) throws Exception {
log.debug("Getting Company Info");
final CompanyInfoDTO companyInformation = loadCompanyInfo(request);
model.addAttribute("companyInformation", companyInformation);
return "companyInfo";
}
private CompanyInfoDTO loadCompanyInfo(final HttpServletRequest request) throws Exception {
final Account someAccount= (Account)request.getAttribute("someAccount");
if (null != someAccount) {
final CompanyInfoDTO companyInformation = companyService.getCompanyInfo(someAccount);
return companyInformation;
} else {
throw new Exception("Unable to retrieve this account details.");
}
}
}
Controller Test -
#RunWith(SpringRunner.class)
#WebMvcTest(CompanyInfoController.class)
#Import(SecurityConfiguration.class)
public class CompanyInfoControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private CompanyService companyService;
#MockBean(name = "someService")
private SomeService someAuthorizationService;
#Before
public void setUp(){
Mockito.when(someAuthorizationService.hasRole(Constants.MY_ROLE))
.thenReturn(true);
}
#Test
#WithMockUser
public void canInvokeGetCompanyInfoServiceSuccessfully() throws Exception{
CompanyInfoDTO dto = new CompanyInfoDTO();
final Account mockAccount = new Account();
mockAccount.setId("12334");
Mockito.when(companyService.getCompanyInfo(Mockito.any(Account.class)))
.thenReturn(dto);
RequestBuilder request = get("/companyInfo")
.requestAttr("someAccount", mockAccount);
mockMvc.perform(get("/companyInfo"))
.andExpect(status().isOk());
}
#Test
#WithMockUser
public void forwardsToCompanyInfoPageSuccessfully() throws Exception{
CompanyInfoDTO dto = new CompanyInfoDTO();
final Account mockAccount = new Account();
mockAccount.setId("12334");
Mockito.when(companyService.getCompanyInfo(Mockito.any(Account.class)))
.thenReturn(dto);
RequestBuilder request = get("/companyInfo")
.requestAttr("someAccount", mockAccount);
mockMvc.perform(request)
.andExpect(redirectedUrl("companyInfo"));
}
}
The test keeps failing with this error -
java.lang.AssertionError: Redirected URL
Expected :companyInfo
Actual :null
Not sure what I am missing!
I do not see the debug statement inside my controller getting printed. But my controller is not mocked out. Is my security mocking correct? Any help is greatly appreciated.
Update - This is what I see in my logs -
MockHttpServletRequest:
HTTP Method = GET
Request URI = /companyInfo
Parameters = {}
Headers = []
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = org.cmt.controller.CompanyInfoController
Method = public java.lang.String org.cmt.controller.CompanyInfoController.getCompanyInfo(javax.servlet.http.HttpServletRequest,org.springframework.ui.Model) throws java.lang.Exception
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", X-Frame-Options:"SAMEORIGIN"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: No ModelAndView found
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:36)
I see Http response code 200. I am assuming it authenticates just fine. But why would my model and view be null?

Testing with Mockito

I'm trying to test some services with Mockito but I have problems when the main class that I test and where I inject Mocks calls to super.
I run the project with spring and these are the steps I follow to get the error.
Here is where I create the test
public class UrlShortenerTests {
private MockMvc mockMvc;
#Mock
private ShortURLRepository shortURLRepository;
#Mock
private ClickRepository clickRespository;
#InjectMocks
private UrlShortenerControllerWithLogs urlShortenerWL;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(urlShortenerWL).build();
}
#Test
public void thatShortenerCreatesARedirectIfTheURLisOK() throws Exception {
mockMvc.perform(post("/link")
.param("url", "http://www.google.com"))
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("$.target", is("http://example.com/")));
}
}
Here is the class UrlShortenerControllerWithLogs with the method shortener, which is the one I want to test with the previous POST call
#RestController
public class UrlShortenerControllerWithLogs extends UrlShortenerController {
#Autowired
private ClickRepository clickRepository;
#Autowired
private ShortURLRepository SURLR;
public ResponseEntity<ShortURL> shortener(#RequestParam("url") String url,
#RequestParam(value = "sponsor", required = false) String sponsor,
#RequestParam(value = "brand", required = false) String brand,
HttpServletRequest request) {
ResponseEntity<ShortURL> su = super.shortener(url, sponsor, brand,
request);
return su;
}
And this is the super class
#RestController
public class UrlShortenerController {
#Autowired
protected ShortURLRepository shortURLRepository;
#Autowired
protected ClickRepository clickRepository;
#RequestMapping(value = "/link", method = RequestMethod.POST)
public ResponseEntity<ShortURL> shortener(#RequestParam("url") String url,
#RequestParam(value = "sponsor", required = false) String sponsor,
#RequestParam(value = "brand", required = false) String brand,
HttpServletRequest request) {
ShortURL su = createAndSaveIfValid(url, sponsor, brand, UUID
.randomUUID().toString(), extractIP(request));
if (su != null) {
HttpHeaders h = new HttpHeaders();
h.setLocation(su.getUri());
return new ResponseEntity<>(su, h, HttpStatus.CREATED);
} else {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
protected ShortURL createAndSaveIfValid(String url, String sponsor,
String brand, String owner, String ip) {
UrlValidator urlValidator = new UrlValidator(new String[] { "http",
"https" });
if (urlValidator.isValid(url)) {
String id = Hashing.murmur3_32()
.hashString(url, StandardCharsets.UTF_8).toString();
ShortURL su = new ShortURL(id, url,
linkTo(
methodOn(UrlShortenerController.class).redirectTo(
id, null)).toUri(), sponsor, new Date(
System.currentTimeMillis()), owner,
HttpStatus.TEMPORARY_REDIRECT.value(), true, ip, null);
return shortURLRepository.save(su);
} else {
return null;
}
}
So, when I call to shortURLRepository.save(su) in the second method (createAndSaveIfValid), it never enters in the method save, so it returns me null instead of the object I want.
The code of the implementation of ShortURLRepository and the method save is:
#Repository
public class ShortURLRepositoryImpl implements ShortURLRepository {
private static final Logger log = LoggerFactory
.getLogger(ShortURLRepositoryImpl.class);
#Override
public ShortURL save(ShortURL su) {
try {
jdbc.update("INSERT INTO shorturl VALUES (?,?,?,?,?,?,?,?,?)",
su.getHash(), su.getTarget(), su.getSponsor(),
su.getCreated(), su.getOwner(), su.getMode(), su.getSafe(),
su.getIP(), su.getCountry());
} catch (DuplicateKeyException e) {
log.debug("When insert for key " + su.getHash(), e);
return su;
} catch (Exception e) {
log.debug("When insert", e);
return null;
}
return su;
}
I think that the problem is that the object ShortURLRepository created in the test class is not initialized on the super class (UrlShortenerController) or something similar.
Is it possible?
Can anybody help me?
The full code is in: https://github.com/alberto-648702/UrlShortener2014
The class UrlShortenerTests is in:
bangladeshGreen/src/test/java/urlshortener2014/bangladeshgreen
The class UrlShortenerControllerWithLogs is in:
bangladeshGreen/src/main/java/urlshortener2014/bangladeshgreen/web
The class UrlShortenerController is in:
common/src/main/java/urlshortener2014/common/web
The class ShortURLRepositoryImpl is in:
common/src/main/java/urlshortener2014/common/repository
This is not an error. This is the expected behaviour. #Mock creates a mock. #InjectMocks creates an instance of the class and injects the mocks that are created with the #Mock. A mock is not a real object with known values and methods. It is an object that has the same interface as the declared type but you control its behaviour. By default the mocked object methods do nothing (e.g. return null). Therefore if ShortURLRepository is mocked and injected in UrlShortenerControllerWithLogs calling save in the injected ShortURLRepository does not call the real code as you expected, it does nothing. If you want to mock the behaviour of save, add the following code in your setup:
when(shortURLRepository.save(org.mockito.Matchers.any(ShortURL.class))).
then(new Answer<ShortURL>() {
#Override
public ShortURL answer(InvocationOnMock invocation) throws Throwable {
ShortURL su = (ShortURL) invocation.getArguments()[0];
// Do something with su if needed
return su;
}
});

Resources