jsonRootName missing while performing unit test case for spring hateoas - java

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

Related

How create unit test in Spring REST in POST Cotroller

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)))

Mockito - Spying on more than one dependency

Is it possible to inject a spy for two dependencies into the tested class?
I have this class:
#Service
#Singleton
#MessageReceiver
public class LinkHitCallbackEventHandler {
public static final Logger logger = LogManager.getLogger();
#Inject
private CallbackInvocationBuilder callbackInvocationBuilder;
#Inject
private CallbackLogBuilder callbackLogBuilder;
#MessageReceiver
public void handle(#SubscribeTo LinkHitEvent event) {
Entity<Form> entity = EntityFormFactory.createFromEvent(event);
this.callbackLogBuilder.build(entity);
Response response = this.callbackInvocationBuilder.post(entity);
}
}
Importantly, it's a simple class with two injected dependencies: callbackInvocationBuilder and callbackLogBuilder.
I test calls to these dependencies in the following way:
#Test
public void send_callback_after_event_is_published() {
target("/testY")
.property("jersey.config.client.followRedirects", false)
.request()
.header("User-Agent", UserAgentMother.allowedUserAagent())
.get();
verify(callbackInvocationBuilder).post(anyObject());
// verify(callbackLogBuilder).build(anyObject());
}
This arrangement the test passes, because I first call callbackLogBuilder.build(entity).
If I swap calls and call them first callbackInvocationBuilder.post(entity), the test will fail.
Both callbackLogBuilder and callbackInvocationBuilder are spies. I configure them in configure () from JerseyTest. In exactly the same way.
One of the method to inject spy to use whitebox and use verify
like this:
Whitebox.setInternalState(ClassToTest.class, "Object", spy1);
, So the test method ill be like this:
#Test
public void send_callback_after_event_is_published() {
Whitebox.setInternalState(LinkHitCallbackEventHandler.class, "callbackInvocationBuilder", spy1);
Whitebox.setInternalState(LinkHitCallbackEventHandler.class, "callbackInvocationBuilder", spy2);
target("/testY")
.property("jersey.config.client.followRedirects", false)
.request()
.header("User-Agent", UserAgentMother.allowedUserAagent())
.get();
verify(callbackInvocationBuilder).post(anyObject());
verify(callbackLogBuilder).build(anyObject());
}
, If you want to capture the argument
ArgumentCaptor<Entity> captor = ArgumentCaptor.forClass(Entity.class);
verify(callbackInvocationBuilder, times(1)).post(captor.capture());
Entity actualEntity = captor.getValue();
assertEquals(expected, actualEntity);

Generate HATEOAS links on Spring MVC methods with optional hyphenated parameters

I'm trying to implement an OGC API - Features service using Spring Boot. The specification includes links to self, parent collections and such, so I figured Spring HATEOAS could help with that. Unfortunately, I'm having trouble with the bbox-crs parameter.
An example demonstration:
#RestController
public class DemoController {
#GetMapping(path = "/link", produces = "application/geo+json")
public Link getLink(#RequestParam(required = false, name = "bbox-crs") String bboxCrs) {
return linkTo(methodOn(DemoController.class).getLink(bboxCrs))
.withSelfRel()
.expand()
.withType("application/geo+json");
}
}
Test:
#SpringBootTest
#AutoConfigureMockMvc
class DemoApplicationTests {
#Autowired
private MockMvc mockMvc;
#Test
public void getLinkWithParam() throws Exception {
String expectedJson = new ObjectMapper().writeValueAsString(
new Link("http://localhost/link?bbox-crs=hello").withType("application/geo+json"));
mockMvc.perform(get("/link?bbox-crs=hello"))
.andExpect(content().contentType("application/geo+json"))
.andExpect(content().json(expectedJson));
}
#Test
public void getLinkNoParam() throws Exception {
String expectedJson = new ObjectMapper().writeValueAsString(
new Link("http://localhost/link").withType("application/geo+json"));
mockMvc.perform(get("/link"))
.andExpect(content().contentType("application/geo+json"))
.andExpect(content().json(expectedJson));
}
}
The first test succeeds, but the second test fails with the error Illegal character in path at index 23: http://localhost/link{?bbox-crs}.
According to issue #799, this is working as intended, as the URI Template spec does not allow for hyphens in variable names. I'm trying to build an URI, though, not an URI Template. Is there some way to
achieve what I'm after? Maybe by configuring Spring or by creating the link in a different way?

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();

Categories