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
Related
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.
I am facing issue related to OffsetDateTimeStamp while writing test case for my webservice. When I test from browser, it's gives right response but while writing test case it's not showing offset and because of that it's failing.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestControllerTest {
#LocalServerPort
private int port;
private WebClient client;
#Test
public void test() {
Person person = new Person();
person.setId(1);
person.setBirthDate(OffsetDateTime.now());
person.setMobile(9090909090L);
person.setName("Tempo");
client = WebClient.create("http://localhost:"+port);
Person response = client.post().uri("/test1")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(person))
.retrieve()
.bodyToMono(Person.class)
.block();
Assertions.assertEquals(person.getBirthDate(), response.getBirthDate());
}
}
Controller Code
#RestController
public class TestController {
#PostMapping("/test1")
public Mono<Person> test1(#RequestBody Person person) {
System.out.println(person.getBirthDate());
return Mono.just(person);
}
}
Mail Application code
#SpringBootApplication
public class TestAppApplication {
public static void main(String[] args) {
SpringApplication.run(TestAppApplication.class, args);
}
#Bean
public Module javaTimeModule() {
return new JavaTimeModule();
}
#Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
}
}
Person.java
public class Person {
private int id;
private String name;
private long mobile;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
private OffsetDateTime birthDate;
public Person() {}
}
application.properties
spring.jackson.serialization.write-dates-as-timestamps=false
spring.jackson.deserialization.adjust-dates-to-context-time-zone=false
spring.jackson.serialization.write-dates-with-zone-id=true
output of test case
org.opentest4j.AssertionFailedError:
Expected :2021-01-04T17:43:51.817+05:30
Actual :2021-01-04T12:13:51.817Z
<Click to see difference>
How is your property birthDate defined in your Person entity class? You need to define the format there. You can do it like this:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
private OffsetDateTime birthDate;
...
public OffsetDateTime getBirthDate() {
return birthDate;
}
See more detailed info in the answers to this question: Spring Data JPA - ZonedDateTime format for json serialization
I'm a beginner on Spring framework, trying to test the Controller.
The funny thing is, using Postman, I got the correct response, but not in JUnit where receive Actual :400 (bad request) instead of Expected :200.
This is due to empty field passengerCount because appears null. The class of the request is different of the response. This latter doesn't have a field for the passenger.
Controller
#Validated
#RestController
#RequestMapping("flights")
public class BusyFlightsController {
CrazyAirDatabase crazyAirService;
#Autowired
public BusyFlightsController(CrazyAirDatabase crazyAirService) {
this.crazyAirService = new CrazyAirDatabase();
}
#RequestMapping(value = "/crazy-air-response", method = RequestMethod.GET, produces = "application/json")
public List<CrazyAirResponse> getCrazyAirResponse(
#Valid CrazyAirRequest crazyAirRequest,
#RequestParam("origin") String origin,
#RequestParam("destination") String destination,
#RequestParam("departureDate") String departureDate,
#RequestParam("returnDate") String returnDate,
#RequestParam("passengerCount") int passengerCount
) {
crazyAirRequest = new CrazyAirRequest(origin, destination, departureDate, returnDate,
passengerCount);
return crazyAirService.getCrazyAirResponse(crazyAirRequest);
}
}
CrazyAirRequest class
public class CrazyAirRequest {
#IATACodeConstraint
private String origin;
#IATACodeConstraint
private String destination;
private String departureDate;
private String returnDate;
private int passengerCount;
public CrazyAirRequest(String origin, String destination, String departureDate,
String returnDate, int passengerCount) {
this.origin = origin;
this.destination = destination;
this.departureDate = departureDate;
this.returnDate = returnDate;
this.passengerCount = passengerCount;
}
// Getters
}
CrazyAirResponse class
public class CrazyAirResponse {
private String airline;
private double price;
private String cabinClass;
private String departureAirportCode;
private String destinationAirportCode;
private String departureDate;
private String arrivalDate;
public CrazyAirResponse(String airline, double price, String cabinClass, String departureAirportCode,
String destinationAirportCode, String departureDate, String arrivalDate) {
this.airline = airline;
this.price = price;
this.cabinClass = cabinClass;
this.departureAirportCode = departureAirportCode;
this.destinationAirportCode = destinationAirportCode;
this.departureDate = departureDate;
this.arrivalDate = arrivalDate;
}
// Getters
}
Repo CrazyAirDatabase
#Component
public class CrazyAirDatabase implements CrazyAirService {
List<CrazyAirResponse> list;
public CrazyAirDatabase() {
list = new ArrayList<>(
Arrays.asList(
new CrazyAirResponse("Ryanair", 125, "E", "LHR",
"BRN", "2018-10-08", "2020-10-08")
);
}
#Override
public List<CrazyAirResponse> getCrazyAirResponse(CrazyAirRequest request) {
return list.stream()
.filter(t -> t.getDepartureAirportCode().equals(request.getOrigin()) &&
t.getDestinationAirportCode().equals(request.getDestination()) &&
t.getDepartureDate().equals(request.getDepartureDate()) &&
t.getArrivalDate().equals(request.getReturnDate())
)
.collect(Collectors.toList());
}
}
Test
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class BusyFlightsControllerTest {
#Autowired
MockMvc mockMvc;
#Mock
CrazyAirRequest crazyAirRequest;
#InjectMocks
private BusyFlightsController controller;
#Mock
CrazyAirService service;
#Before
public void before() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testino() throws Exception {
crazyAirRequest = new CrazyAirRequest("LHR",
"BRN", "2018-10-08", "2020-10-08", 120);
List<CrazyAirResponse> crazyAirResponse = Arrays.asList(new CrazyAirResponse("Ryanair", 125,
"E", "LHR",
"BRN", "2018-10-08", "2020-10-08")
);
when(service.getCrazyAirResponse(crazyAirRequest)).thenReturn(crazyAirResponse);
ObjectMapper objectMapper = new ObjectMapper();
String airplane = objectMapper.writeValueAsString(crazyAirResponse);
ResultActions result = mockMvc.perform(get("/flights/crazy-air-response")
.contentType(MediaType.APPLICATION_JSON)
.content(airplane)
);
result.andExpect(status().isOk());
}
}
If I put this:
ResultActions result = mockMvc.perform(get("/flights/crazy-air-response?origin=LHR&destination=CTA&departureDate=some&returnDate=some&passengerCount=1")
.contentType(MediaType.APPLICATION_JSON)
.content(airplane)
);
Test is passed.
Then, need I perform Postman first, and after to copy and paste the query to pass the test?
I need to use #Service from another Spring project. I will need a REST call to invoke it but how am I suppose to do it?
This is the service:
#Service
public class LocationsService implements ILocationsService {
private final Logger logger = LoggerFactory.getLogger("LocationsService");
private final ILocationRepository locationRepository;
private final IEvseRepository evseRepository;
private final IConnectorRepository connectorRepository;
#PersistenceContext
private EntityManager entityManager;
#Autowired
public LocationsService(ILocationRepository locationRepository, IEvseRepository evseRepository, IConnectorRepository connectorRepository, EntityManager entityManager) {
this.locationRepository = locationRepository;
this.evseRepository = evseRepository;
this.connectorRepository = connectorRepository;
this.entityManager = entityManager;
}
public Location getLocation(String countryCode, String partyId, String id) {
return locationRepository.findByCountryCodeAndPartyIdAndId(countryCode, partyId, id);
}
public Location deleteLocation(String countryCode, String partyId, String id) {
Location location = locationRepository.findByCountryCodeAndPartyIdAndId(countryCode, partyId, id);
if (location == null) {
logger.info("Location does not exist.");
return null;
}
locationRepository.deleteById(location.getLocId());
return location;
}
I need to call the service in this controller. The controller is in a different project:
#RestController
#RequestMapping(value = "/locations", produces = MediaType.APPLICATION_JSON_VALUE)
#Api(tags = "Locations management")
public class LocationController {
#Autowired
private LocationsService locationsService;
#RequestMapping(method = RequestMethod.GET , produces = MediaType.APPLICATION_JSON_VALUE)
#ApiOperation(value = "Get Locations", notes = "Get locations", nickname = "getLocations",
authorizations = #Authorization(value = "Bearer"))
public ResponseEntity<List<LocationDto>> getLocations() {
List<LocationDto> locations = new ArrayList<>();
return new ResponseEntity<>(locations, HttpStatus.OK);
}
}
I searched for solutions but found nothing helpful and will appreciate any help.
Thank you!
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;
}
});