While working on RestAssured I came across the concept of Serialization and DeSerialization(POJO Classes) to read and validate the response. I went through some tutorial and was able to create the POJO class based on my response.
However, when I use the POJO class reference in my Tests I am not able to use the then() block for different assertions. Below details might clear things bit more :
TestMethod without POJO :
public void listUsers() {
RestAssured.baseURI="https://reqres.in/";
Response res = RestAssured.given()
.contentType("application/json")
.queryParam("page", 2)
.when()
.get("/api/users")
.then()
.assertThat().statusCode(200).and()
.body("page", Matchers.equalTo(2)).and()
.body("total", Matchers.greaterThanOrEqualTo(1))
.body("data.email", Matchers.hasItem("george.edwards#reqres.in"))
.extract().response();
JsonPath jsonpath = new JsonPath(res.asString());
System.out.println(jsonpath.get("data[0].email"));
}
Test Method with POJO :
public void listUserswithPOJO() {
RestAssured.baseURI="https://reqres.in/";
ListUsers res = RestAssured.given()
.contentType("application/json")
.queryParam("page", 2)
.when()
.get("/api/users").as(ListUsers.class);
System.out.println(res.getData().get(1).getEmail());
}
Test Class :
#Test
public void listUsersTest() {
ReqResApi TS1 = new ReqResApi();
TS1.listUserswithPOJO();
}
I want to keep the assertions of the then block as it is while using POJO classes as well. When I try to do so after as(ListUser.class), it gives the compilation error that then() is undefined for ListUser class.
Is there any way in which I can use both POJO class as well as then() block in my rest assured tests.
This is not possible because Return types of these options are different.
MainPojo m1 =RestAssured.given().contentType("application/json").queryParam("page", 2).when().get("/api/users")
.as(MainPojo.class)==> Return Type is ur Class, in this example Main Pojo
System.out.println(m1.getData().get(0).getFirst_name());
RestAssured.given().contentType("application/json").queryParam("page", 2).when()
.get("/api/users").then().assertThat().statusCode(200).and().body("page", Matchers.equalTo(2)).and()
.body("total", Matchers.greaterThanOrEqualTo(1))
.body("data.email", Matchers.hasItem("george.edwards#reqres.in")).extract().response();---> Return Type is Response
Related
I am a beginner to the spring webflux. We are currently migrating our application to Spring Webflux. No I have stuck with a problem. The following is my scenario.
The main service class is calling the following service classes for data
StudentService - return Mono<Student>
StaffService - return Mono<Staff>
Here I have a wrapper class StudentWithMentor to store the result from these service classes.
public class StudentWithMentor {
private Student student;
private Staff mentor;
}
Now in controller I am calling the above 2 services and map it into 'StudentWithMentor' in the following way
Mono<StudentWithMentor> studentWithMentorMono = Mono.just(new StudentWithMentor());
return studentWithMentorMono.map(s->{
studentService.getStudentById(id)
.doOnSuccess(s::setStudent)
.doOnSuccess(st->staffService.getStaffByGrade(st.getGrade()));
return s;
});
But when I call this endpoint I am getting the following result in postman
{
"student": null,
"mentor": null
}
Note: I am getting result from the underlying services when I debugg. But the call is returning before it process.
How can I achieve this in a complete non-blocking way.
Appreciates any help.
The easiest way will be to to use a zipWith operator to merge the results into StudentWithMentor object.
See the code below:
Mono<StudentWithMentor> studentWithMentorMono = studentService.getStudentById(id)
.zipWhen(student -> staffService.getStaffByGrade(student.getGrade()), StudentWithMentor::new);
I have a service implemented with Dropwizard and I need to dump incorrect requests somewhere.
I saw that there is a possibility to customise the error message by registering ExceptionMapper<JerseyViolationException>. But I need to have the complete request (headers, body) and not only ConstraintViolations.
You can inject ContainerRequest into the ExceptionMapper. You need to inject it as a javax.inject.Provider though, so that you can lazily retrieve it. Otherwise you will run into scoping problems.
#Provider
public class Mapper implements ExceptionMapper<ConstraintViolationException> {
#Inject
private javax.inject.Provider<ContainerRequest> requestProvider;
#Override
public Response toResponse(ConstraintViolationException ex) {
ContainerRequest request = requestProvider.get();
}
}
(This also works with constructor argument injection instead of field injection.)
In the ContainerRequest, you can get headers with getHeaderString() or getHeaders(). If you want to get the body, you need to do a little hack because the entity stream is already read by Jersey by the time the mapper is reached. So we need to implement a ContainerRequestFilter to buffer the entity.
public class EntityBufferingFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
ContainerRequest request = (ContainerRequest) containerRequestContext;
request.bufferEntity();
}
}
You might not want this filter to be called for all requests (for performance reasons), so you might want to use a DynamicFeature to register the filter just on methods that use bean validation (or use Name Binding).
Once you have this filter registered, you can read the body using ContainerRequest#readEntity(Class). You use this method just like you would on the client side with Response#readEntity(). So for the class, if you want to keep it generic, you can use String.class or InputStream.class and convert the InputStream to a String.
ContainerRequest request = requestProvider.get();
String body = request.readEntity(String.class);
i have post method in rest controller and i want to create a test for this method:
This is my method:
#PostMapping("/persons")
public ResponseEntity<PersonDto> createPerson(#RequestBody PersonDto personDto) {
try {
personService.createPerson(personDto);
return ResponseEntity.ok(personDto);
} catch (Exception e) {
return ResponseEntity.badRequest().build();
}
}
I have no idea how this test with mock should look like.
As you use Spring, I recommend you to use #WebMvcTest to mock all things but the Controller under test.
You should so explicitly mock the dependencies of this Controller. In your case, mocking the personService field is enough.
In your test class declare a personService field annotated with #MockBean to create a Mockito mock of the class that will be added to the Spring context.
Then record a behavior for this mock.
You have two branches here : it works and an exception is risen.
So you could define two test method and record a specific behavior in each one.
At last assert the gotten response from the controller.
I already have test for deleting method. Yes i use springboot, jpa, rest, h2:
#Test
public void shouldDeletePersonById() throws Exception {
Mockito.doCallRealMethod().when(personService).deleteById(1L);
mockMvc.perform(delete("/persons/{id}", 1L)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound());
}
But for create person this looks for me very hard. I don't know what i should call of in posy method(like in delete i call id to find what i want to delete).
I am using Spring 4 + Jackson 2 and have written a fully functional POST method using #RequestBody on a custom class. This method has no trouble unmarshalling the object.
#ResponseBody
#RequestMapping(value="store", method = RequestMethod.POST)
public ServiceResponse store(#RequestBody CustomClass list) {
...
}
// Request: { code: "A", amount: 200 }
When I attempted to add another method to handle a collection of the same class instead, my POST requests were returning with the following error.
HTTP Status 400: The request sent by the client was syntactically incorrect.
I note that this error typically occurs when the JSON submitted does not match the entity class. However, all I am doing is submitting an array of the same object instead of the object itself, which has already proven to work.
#ResponseBody
#RequestMapping(value="store-bulk", method = RequestMethod.POST)
public ServiceResponse storeBulk(#RequestBody List<CustomClass> list) {
...
}
// Request: [{ code: "A", amount: 200 }, { code: "B", amount: 400 }]
Am I missing something here?
In Java, type information for generics is erased at runtime, so Spring sees your List<CustomClass> object as List<Object> object, thus it cannot understand how to parse it.
One of ways to solve it, you could capture the type information by creating a wrapper class for your list, like this:
public class CustomClassList extends ArrayList<CustomClass> {
}
Sergey is right that the issue is due to type erasure. Your easiest way out is to bind to an array, so
#ResponseBody
#RequestMapping(value="store-bulk", method = RequestMethod.POST)
public ServiceResponse storeBulk(#RequestBody CustomClass[] object) {
...
}
The answer is that Spring 4 doesn't actually get rid of type erasure, contrary to what some other solutions suggest. While experimenting on debugging via manual unmarshalling, I decided to just handle that step myself instead of an implicit cast that I have no control over. I do hope someone comes along and proves me wrong, demonstrating a more intuitive solution though.
#ResponseBody
#RequestMapping(value="store-bulk", method = RequestMethod.POST)
public ServiceResponse storeBulk(#RequestBody String json) {
try {
List<CustomClass> list = new ObjectMapper().readValue(json, new TypeReference<List<CustomClass>>() { });
...
} catch (Exception e) {
...
}
}
Bonus: Right after I got this working, I bumped into this exception:
IllegalStateException: Already had POJO for id
If anyone gets this, it's because the objects in the list happen to reference some object that another item in the list already references. I could work around this since that object was identical for my entire collection, so I just removed the reference from the JSON side from all but the first object. I then added the missing references back after the JSON was unmarshalled into the List object.
Two-liner for the Java 8 users (the User object reference was the issue in my case):
User user = list.get(0).getUser();
list.stream().filter(c -> c.getUser() == null).forEach(t -> t.setUser(user));
By leveraging the Testing with async queries section of the Testing with a Mocking Framework article on MSDN, I've been able to create many successfully passing tests.
Here's my test code, which uses NSubstitute for mocks:
var dummyQueryable = locations.AsQueryable();
var mock = Substitute.For<DbSet<Location>, IDbAsyncEnumerable<Location>, IQueryable<Location>>();
((IDbAsyncEnumerable<Location>)mock).GetAsyncEnumerator().Returns(new TestDbAsyncEnumerator<Location>(dummyQueryable.GetEnumerator()));
((IQueryable<Location>)mock).Provider.Returns(new TestDbAsyncQueryProvider<Location>(dummyQueryable.Provider));
((IQueryable<Location>)mock).Expression.Returns(dummyQueryable.Expression);
((IQueryable<Location>)mock).ElementType.Returns(dummyQueryable.ElementType);
((IQueryable<Location>)mock).GetEnumerator().Returns(dummyQueryable.GetEnumerator());
sut.DataContext.Locations = mock;
var result = await sut.Index();
result.Should().BeView();
sut.Index() doesn't do much, but it makes the following query:
await DataContext.Locations
.GroupBy(l => l.Area)
.ToListAsync());
This works fine until I add a projection into the query:
await DataContext.Locations
.GroupBy(l => l.Area)
.Select(l => new LocationsIndexVM{ Area = l.Key }) // added projection
.ToListAsync());
which results in this exception:
System.InvalidOperationException
The source IQueryable doesn't implement IDbAsyncEnumerable<LocationsIndexVM>. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068.
at System.Data.Entity.QueryableExtensions.AsDbAsyncEnumerable(IQueryable`1 source)
at System.Data.Entity.QueryableExtensions.ToListAsync(IQueryable`1 source)
at Example.Web.Controllers.HomeController.<Index>d__0.MoveNext() in HomeController.cs: line 25
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Example.Test.Web.Controllers.HomeControllerShould.<TempTest>d__4.MoveNext() in HomeControllerShould.cs: line 71
UPDATE: I've uploaded a small, simple solution that reproduces this problem.
Can anyone provide an example of what is required to unit test a query that is both async and contains a .Select() projection?
So I did a bit of digging, and the issue is to do with the way the TestDbAsyncEnumerable<T> exposes the IQueryProvider. My best guess as to the reasoning is below, and the solution below that.
TestDbAsyncEnumerable<T> inherits from EnumerableQuery<T>, which in turn inherits from IQueryable<T>, and explicitly implements the Provider property of this interface:
IQueryProvider IQueryable.Provider { get ... }
Given that it's implemented explicitly, I am assuming that the LINQ internals explicitly cast a type before trying to get the Provider:
((IQueryable<T>)source).Provider.CreateQuery(...);
I don't have a source on hand (and can't be bothered looking for one), but I believe the type binding rules are different for explicit implementations; essentially, the Provider property on your TestDbAsyncEnumerable<T> is not considered to be an implementation of IQueryable<T>.Provider as an explicit one exists further up the chain, so your TestDbAsyncQueryProvider<T> is never returned.
The fix for this is to make TestDbAsyncEnumerable<T> also inherit IQueryable<T> and explicitly implement the Provider property, as below (adjusted from the MSDN article you linked):
public class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable)
{ }
public TestDbAsyncEnumerable(Expression expression) : base(expression)
{ }
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider
{
get { return new TestDbAsyncQueryProvider<T>(this); }
}
}