Match LocalDateTime using Hamcrest in MVC Test - java

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

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 mock same method calls with different collection-arguments

I try to mock same method calls with different collection-arguments.
My problem is that im not getting the correct mocked-answer from Mocked-Call for the input.
Test-Class:
#ExtendWith(SpringExtension.class)
public class CollectionTest {
#MockBean
private Controller c;
#BeforeEach
public void init() {
Collection<String> a = Mockito.anyCollection();
a.add("a");
Mockito.when(c.run(a)).thenReturn("a");
Collection<String> b = Mockito.anyCollection();
b.add("b");
Mockito.when(c.run(b)).thenReturn("b");
}
#Test
public void test() {
assertEquals("a", c.run(Lists.newArrayList("a"))); // DOESNT'WORK!!! Returns "b" but should "a"
assertEquals("b", c.run(Lists.newArrayList("b"))); //
}
}
Controller-Class:
#Service
public class Controller{
public String run(Collection<String> c) {
return "not-mocked";
}
}
I'v got no idea why it doesn't return "a". I tried to change the collection to string but same behaviour.
What are the Steps to do, to get the following behaviour?
#Test
public void test() {
assertEquals("a", c.run(Lists.newArrayList("a"))); // should return "a"
assertEquals("b", c.run(Lists.newArrayList("b"))); // should return "b"
}
Im using Java Mockito "3.1" and Spring, but I think Mockito is the important information here.
Your second call - Mockito.when(c.run(b)).thenReturn("b");
is overruling our first call so Mockito will therefore always return "b".
If you need multiple answers from the same call, you can use the varags variant:
when(c.run(anyCollection())).thenReturn("a", "b");
Now the first call to the controller's run method will return "a" and all subsequent calls will return "b". You can provide as many return results as you want and the last one will be repeated from then on as the answer.
Write two tests will show you the results you are expecting.
You are adding to the same Controller two different results so you get only the last one : Mockito.when(c.run(b)).thenReturn("b");
Normal. The last mocked expected result in your setUp() will stay in memory.
Previous answer was :
You can use something like junit and mockito to test your spring-web-mvc application.
It looks like that :
#WebMvcTest(controllers = UserController.class)
#ActiveProfiles("test")
class UserControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private UserService userService;
private List<User> userList;
#BeforeEach
void setUp() {
this.userList = new ArrayList<>();
this.userList.add(new User(1L, "user1#gmail.com", "pwd1","User1"));
this.userList.add(new User(2L, "user2#gmail.com", "pwd2","User2"));
this.userList.add(new User(3L, "user3#gmail.com", "pwd3","User3"));
}
}
And as an example :
#Test
void shouldFetchAllUsers() throws Exception {
given(userService.findAllUsers()).willReturn(userList);
this.mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.size()", is(userList.size() )));
}
Example from #see https://medium.com/backend-habit/integrate-junit-and-mockito-unit-testing-for-controller-layer-91bb4099c2a5

Problems with null pointer using mockito

I'm trying to test a method. And in this method, a new Object is instancied, but I don't want it, otherwise other class will be tested.
How I tell to mockito dont intanciate it?
#Component
#EnableScheduling
public class VerificadorDeNovasAssinaturas {
private DocuSign docuSign;
private ApiClient apiClient;
#Autowired
private DocuSignProperties docuSignProperties;
public EnvelopesInformation verificaNovasAssinaturas() throws Exception {
this.docuSign = new DocuSign(docuSignProperties); // I don't want mockito instanciate DocuSign
this.apiClient = docuSign.getApiClient();
this.apiClient.addDefaultHeader("Authorization", "Bearer " + docuSign.getoAuthToken().getAccessToken());
And my test class:
#SpringBootTest
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
public class VerificadorDeNovasAssinaturasTest {
#InjectMocks
private VerificadorDeNovasAssinaturas verificador;
private DocuSignProperties docuSignProperties;
private ApiClient apiClient;
private UserInfo userInfo;
private OAuthToken oAuthToken;
#Mock
private DocuSign docuSign;
#Before
public void initialize() throws Exception {
docuSignProperties = new DocuSignProperties();
docuSignProperties.setBaseUrl("https://demo.docusign.net/restapi");
docuSignProperties.setBasePath("/restapi");
setApiClientConfigurations();
when(docuSign.getApiClient()).thenReturn(this.apiClient);
when(docuSign.getoAuthToken()).thenReturn(this.oAuthToken);
...}
private void setApiClientConfigurations() throws Exception {
this.apiClient = new ApiClient(this.docuSignProperties.getBaseUrl());
this.oAuthToken = getOAuth();
... }
#Test
public void testaVerificacaoDeNovasAssinaturas() throws Exception {
EnvelopesInformation results = verificador.verificaNovasAssinaturas();
assertNotNull(results);
}
I don't want mockito instanciate a new DocuSign, because this is not the reason of the test. There is some way do ignore this step?
Well, Mockito can not change something if your code( Code to be tested, Which you intend to) does something, However you can mock it so that it does not create a new object (rather have your "Mocked Object"), so that you can verify something against the expected behavior.
In your code if you change few lines , you can achieve what you want, like -
Create a DocuSignService class and there you create this new object in say some - getDocuSign method. Then your code looks something like below -
#Autowired
private DocuSignService docuSignService ;
this.docuSign = new DocuSign(docuSignProperties); // This is what you have
this.docuSign = this.docuSignService.getDocuSign() ; // This is new code
Now in your test case -
#Mock
DocuSignService docuSignService ;
#Mock
private DocuSign docuSign;
//.
//.
Mockito.when(this.docuSignService.getDocuSign()).thenReturn(docuSign);
Now you have control on this object.
I resolved it using powerMockito.
DocuSign docuSign = PowerMockito.mock(DocuSign.class);
PowerMockito.whenNew(DocuSign.class).withAnyArguments().thenReturn(docuSign);

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

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