How create unit test in Spring REST in POST Cotroller - java

I have a Spring Boot Controller with POST. Method works fine. I tested it by POSTMAN and from postgresql I recieved JSON. But I need test it.
#RestController
#RequestMapping("/api")
public class FamilyController {
private final FamilyService familyService;
public FamilyController(FamilyService familyService) {
this.familyService = familyService;
}
#GetMapping("/getFamily/{familyId}")
public List<FamilyMember> getFamily(#PathVariable Integer familyId) {
return familyService.searchFamilyMember(familyId);
}
}
I created test:
#ExtendWith(SpringExtension.class)
#WebMvcTest(FamilyController.class)
class FamilyControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#MockBean
private FamilyService service;
#Captor
private ArgumentCaptor<Family> argumentCaptor;
#Test
void createFamily() throws Exception {
Family family = new Family();
family.setId(1);
family.setFamilyName("Kowal");
family.setNrOfInfants(2);
family.setNrOfChildren(2);
family.setNrOfAdults(1);
Mockito.when(service.saveAndFlush(argumentCaptor.capture())).thenReturn(1);
mockMvc.perform(post("/api/createFamily")
.contentType(MediaType.APPLICATION_JSON)
.contentType(objectMapper.writeValueAsString(family)))
.andExpect(status().isCreated());
// .andExpect(header().exists("Location"))
// .andExpect(header().string("Location", "http://localhost/api/getFamily/1"));
assertThat(argumentCaptor.getValue().getFamilyName(), is("Kowal"));
}
when I run a test, I received error like below:
WARN 15404 --- [ main] .w.s.m.s.DefaultHandlerExceptionResolver :
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException:
Invalid mime type "{"id":1,"familyName":"Kowal","nrOfInfants":2,"nrOfChildren":2,"nrOfAdults":1,"familyMembers":[]};charset=UTF-8":
does not contain '/']
What am I doing wrong?

The problem is this line:
.contentType(objectMapper.writeValueAsString(family)))
Right now you are sending the object in the content-type header, that's why you get the error message.
Change it to:
.content(objectMapper.writeValueAsString(family)))

Related

StackOverflow error on unit testing API controllers?

I'm working on a Spring Boot MVC project and I'm getting the following error when unit testing my CRUD controllers :
java.lang.StackOverflowError
at java.base/java.lang.Throwable.getOurStackTrace(Throwable.java:840)
at java.base/java.lang.Throwable.getStackTrace(Throwable.java:832)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:55)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60)
java.lang.StackOverflowError
at java.base/java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:834)
at java.base/java.lang.StringBuilder.append(StringBuilder.java:247)
at ch.qos.logback.classic.pattern.ThrowableProxyConverter.subjoinSTEPArray(ThrowableProxyConverter.java:216)
at ch.qos.logback.classic.pattern.ThrowableProxyConverter.recursiveAppend(ThrowableProxyConverter.java:161)
at ch.qos.logback.classic.pattern.ThrowableProxyConverter.recursiveAppend(ThrowableProxyConverter.java:168)
Here's one of the controllers unit test, the others follow the same logic.
#Autowired
private MockMvc mockMvc;
#MockBean
private PessoaRepository pessoaRepository;
#Test
public void deletarPessoa_quandoDelete() throws Exception {
Pessoa pessoa = new Pessoa();
pessoa.setNome("Test Nome");
pessoa.setId(89L);
doNothing().when(pessoaRepository).delete(pessoa);
mockMvc.perform(delete("/pessoas/" + pessoa.getId().toString())
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent());
}
What could this be? I have no idea why I'm getting this error.
EDIT : here's the controller, it basically calls for the repo to delete a Pessoa object
#DeleteMapping("/{id}")
#ResponseStatus(HttpStatus.NO_CONTENT)
public void deletar(#ApiParam(value = "ID", example = "1") #PathVariable Long id) {
Pessoa pessoaDeletada = pessoaRepository.getOne(id);
pessoaRepository.delete(pessoaDeletada);
}

Match LocalDateTime using Hamcrest in MVC Test

I'm struggeling implementing a Spring MVC Controller Test for a LocalDateTime field using Mockito and Hamcrest.
#WebMvcTest(controllers = FooController.class)
class FooControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private FooService fooService;
#Test
void testController() throws Exception {
Foo foo = new Foo(LocalDateTime.now());
List<Foo> allFoos = Arrays.asList(foo);
given(fooService.getAllFoos()).willReturn(allFoos);
mockMvc.perform(get("/foos").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].timeOfUpdate", equalTo(foo.getTimeOfUpdate()), LocalDateTime.class));
}
}
The Controller returns a JSON serialized Object Foo which looks like that:
#Getter
#Setter
#AllArgsConstructor
class Foo {
private LocalDateTime timeOfUpdate;
}
The JSON Response looks like this:
[
{
"timeOfUpdate":"2020-08-05T18:40:30.7416748"
}
]
However when I'm trying to Match the field timeOfUpdate with equalTo I recieve the following error:
java.lang.AssertionError: JSON path "$[0].timeOfUpdate"
Expected: <2020-08-05T18:40:30.741674800>
but: was "2020-08-05T18:40:30.7416748"
So It seems to be a precision error, but I have no clue how to resolve this.
Update:
I've changed the Matcher to the following:
andExpect(jsonPath("$[0].timeOfUpdate").value(foo.getTimeOfUpdate()));
Now it seems to coerce the types right but the assertion still fails:
java.lang.AssertionError: JSON path "$[0].timeOfUpdate" expected:<2020-08-05T19:19:05.739893500> but was:<2020-08-05T19:19:05.7398935>
Expected :2020-08-05T19:19:05.739893500
Actual :2020-08-05T19:19:05.7398935
Update 2
Here's one workaround to get the tests green:
.andExpect(jsonPath("$[0].statusRecord.timeOfUpdate", is(person.getStatusRecord().getTimeOfUpdate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'hh:mm:ss")))));
#Getter
#Setter
#AllArgsConstructor
class Foo {
#JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd'T'hh:mm:ss")
private LocalDateTime timeOfUpdate;
}
You just need to use the toString() for .value(foo.getTimeOfUpdate().toString())
as jsonPath("$[0].timeOfUpdate" is json string.
I have also faced the same issue.
Here is your test class looks like after the change.
#WebMvcTest(controllers = FooController.class)
class FooControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private FooService fooService;
#Test
void testController() throws Exception {
Foo foo = new Foo(LocalDateTime.now());
List<Foo> allFoos = Arrays.asList(foo);
given(fooService.getAllFoos()).willReturn(allFoos);
mockMvc.perform(get("/foos").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].timeOfUpdate").value(foo.getTimeOfUpdate().toString()));
}
}

Getting blank response in spring rest controller unit test cases

I have written unit test cases for a spring rest controller for below put method
#PutMapping("/offers/{jobTitle}")
public Offer updateOffer(#PathVariable String jobTitle,#Valid #RequestBody Offer offer) {
return offerService.updateNoOfPost(jobTitle, offer);
}
Below is my service class
#Override
public Offer updateNoOfPost(String jobTitle, Offer offer) {
if(!offerRepository.existsById(jobTitle))
throw new ResourceNotFoundException("JobTitle "+jobTitle+" not found !!");
offer.setNoOfPost(offer.getNoOfPost());
return offerRepository.save(offer);
}
I have written the unit test case for this method using testNg and mockito
public class OfferControllerTest {
private MockMvc mvc;
private JacksonTester<Offer> jsonOffer;
#Mock
private OfferService service;
#InjectMocks
OfferController offerController;
private Offer offer;
#BeforeMethod
public void setup() {
offer = new Offer("LSE", new Date(),1);
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(offerController)
.build();
JacksonTester.initFields(this, new ObjectMapper());
}
#Test
public void updateOffer() throws Exception {
Mockito.when(service.updateNoOfPost("LSE", offer)).thenReturn(offer);
MockHttpServletResponse response = mvc.perform(
put("/offers/LSE").contentType(MediaType.APPLICATION_JSON).content(
jsonOffer.write(new Offer("SE", new Date(), 19)).getJson()
)).andReturn().getResponse();
assertThat(response.getContentAsString()).isEqualTo(new ObjectMapper().writeValueAsString(offer));
}
I am getting response code as 200. but getting blank body.pls find error below
FAILED: updateOffer
org.junit.ComparisonFailure: expected:<"[{"jobTitle":"LSE","createdAt":"2018-10-27","noOfPost":1}]"> but was:<"[]">
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
what am i missing ? is this the standard way of writting unit test cases for spring rest controller ?
Below mocking should fix the issue
Mockito.when(service.updateNoOfPost(Mockito.any(String.class), Mockito.any())).thenReturn(offer);
Read more here: stack-46914252

Cannot inject service into Spring test

financialReportService is null in REST controller that denotes it fails to inject.
Test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SnapshotfindocApp.class)
public class FindocResourceIntTest {
#Inject
private FinancialReportService financialReportService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
FindocResource findocResource = new FindocResource();
ReflectionTestUtils.setField(findocResource, "findocRepository", findocRepository);
this.restFindocMockMvc = MockMvcBuilders.standaloneSetup(findocResource)
.setCustomArgumentResolvers(pageableArgumentResolver)
.setMessageConverters(jacksonMessageConverter).build();
}
#Test
#Transactional
public void getFinancialRecords() throws Exception {
// Get all the financial-reports
restFindocMockMvc.perform(get("/api/financial-reports"))
.andExpect(status().isOk());
List<Findoc> finReports = financialReportService.getFinancialReports();
for (Findoc fr : finReports) {
assertThat(fr.getNo_months()).isBetween(12, 18);
LocalDate documentTimeSpanLimit = LocalDate.now().minusMonths(18);
assertThat(fr.getFinancial_date()).isAfterOrEqualTo(documentTimeSpanLimit);
}
}
The service:
#Service
#Transactional
public class FinancialReportService {
private final Logger log = LoggerFactory.getLogger(FinancialReportService.class);
#Inject
private FinancialReportDAO financialReportDAO;
public List<Findoc> getFinancialReports(){
return financialReportDAO.getFinancialReports();
}
}
Controller:
#GetMapping("/financial-reports")
#Timed
public List<Findoc> getFinancialReports() {
log.debug("REST request to get financial records");
return financialReportService.getFinancialReports(); // financialReportService is null
}
Update:
The application is generated by JHipster. Then the new service and DAO files were added to enable custom database queries to H2.
After #Injecting the service, you also need to set the field in the setup() method. Adding the below line should solve your problem
ReflectionTestUtils.setField(findocResource, "financialReportService", financialReportService);
On a separate note, the following part of the test looks weird. You are fetching the financial reports twice. This file is the FindocResourceIntTest, so I would remove any direct calls to financialReportService.
// Get all the financial-reports
restFindocMockMvc.perform(get("/api/financial-reports"))
.andExpect(status().isOk());
List<Findoc> finReports = financialReportService.getFinancialReports();

jsonRootName missing while performing unit test case for spring hateoas

I have developed an rest service using spring-boot-starter-Hateoas, and I am able to get the json output properly as shown below:
"_embedded": {
"bills":
{
uid: "123"
code: "0000"
And I need to write unit-test case for the same using mockito. The code I have written is as below.
ApplicationTest.java:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class ApplicationTest {
BillControllerAutoTest:
public class BillControllerAutoTest {
private BillService mockBillService;
private MockMvc mockMvc;
private static final String BILL_UID = "99991";
#Before
public void setupController() {
mockBillService= mock(BillService .class);
BillController controller = new BillController (mockBillService);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testGetBills() throws Exception {
// some fake data
final List<Bill> fakeBillList= new ArrayList<>();
fakeBillList.add(BillFake.bill("1234"));
when(mockBillService.getBills(BILL_UID))
.thenReturn(fakeBillList.stream());
// execute and verify
mockMvc.perform(get("/bills/" + BILL_UID ))
.andExpect(content().string(containsString("\"embedded\":{\"bills\"")))
BillController.java:
#RestController
#RequestMapping(value = "/bills/{billUid}", produces = "application/hal+json")
public class BillController extends BaseController {
private BillService billService;
#RequestMapping(method = RequestMethod.GET, value = "")
public ResponseEntity<Resources<Resource<Bill>>> getBills(#PathVariable String billUid) {
return resourceListResponseEntity(
() -> billService.getBills(billUid),
bill-> createResource(billUid),
resources -> resources.add(linkTo(methodOn(BillController .class)
.getBills(billUid)).withSelfRel()));
}
Dependencies:
dependencies {
compile "org.springframework.boot:spring-boot-starter-hateoas"
compile "org.springframework.boot:spring-boot-starter-ws"
compile "org.springframework.boot:spring-boot-starter-actuator"
testCompile("org.springframework.boot:spring-boot-starter-test")
}
My build is failing with the following stackTrace:
java.lang.AssertionError: Response content
Expected: a string containing "\"_embedded\":{\"bills\""
but: was
"content":[
{
uid: "123"
code: "0000"
This means "_embedded : { bills" is not available in the response returned by mockMvc of the unit test. Am I missing any configuration, kindly let me know. Any help would be greatly appreciated.
I've answered very similar question here: Spring's MockMVC Responses Don't Match Browser Response
In a nutshell: spring HATEOAS adds additional configuration for rendering hal properly (as described here: http://docs.spring.io/spring-hateoas/docs/0.19.0.RELEASE/reference/html/#configuration). In your tests you have to apply this configuration manually. Check the first link for details on how to do it

Categories