Im working in a project using Spring Cloud Contract and Wiremock to create integration tests. Everything was fine until we need to add multiple responses to the same request in Wiremock. Reading some docs, i think Scenarios from Wiremock is the way to go.... but since we are using the 'new' #AutoConfigureWiremock annotation, i can't figure out how to work with wiremock scenarios...
Doesnt matter what i do, wiremock always fallback to the 'Started' Scenario everytime. I can't use my 'Expired' Scenario....
My BaseClassCOnfiguration
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
#AutoConfigureWireMock
#AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.LOCAL)
#Import(value = TestConfigClass.class)
#ContextConfiguration(initializers = {Properties.class})
#AutoConfigureMockMvc(addFilters = true)
public abstract class SpringCloudContractBase {
My WireMockClassRule
#ClassRule
public static WireMockClassRule wiremock =
new WireMockClassRule(options()
.dynamicPort()
.extensions(new ResponseTemplateTransformer(true)));
My mapping scenarios
"mappings": [
{
"priority": 1,
"scenarioName": "att",
"requiredScenarioState": "Started",
"request": {
"method": "GET",
"urlPath": "/v1/authtoken/exchange"
},
"response": {
"headers": {
"Content-Type": "application/json"
},
"status": 200,
"bodyFileName": "{{request.pathSegments.[1]}}/{{request.pathSegments.[2]}}/index.json"
}
},
{
"priority": 2,
"scenarioName": "att",
"requiredScenarioState": "Expired",
"request": {
"method": "GET",
"urlPath": "/v1/authtoken/exchange"
},
"response": {
"status": 403
}
}
]
}
And finally my test
#Test
public void test__stub__return403() {
// THIS DOES NOT WORK ! Will pick the first scenario under my mappings instead.....
wiremock.stubFor(get(anyUrl()).inScenario("att").whenScenarioStateIs("Expired"));
Auth auth = service.exchange("token");
}
How can i achieve this ?
Thank you !
Related
Using the PACT-builder for consumer in a consumer-driven test, I try to build a pact and generate the contract as a json file (in a target folder by default).
#ExtendWith(PactConsumerTestExt.class)
#PactTestFor(providerName = "foo-provider")
class FooContract {
#Pact(consumer = "foo-consumer")
RequestResponsePact createPact(PactDslWithProvider builder) {
return builder.given("A server error")
.uponReceiving("Get all Foos")
.path("/foos")
.willRespondWith()
.status(500)
.body("{"title":"Internal Server Error"}")
.toPact()
}
#Test
void getAllFoos(MockServer mockServer) throws IOException, URISyntaxException {
var HttpResponse response = Request.Get(new URIBuilder(mockServer.getUrl() + "/foos").build()).execute().returnResponse();
assertThat(response.getStatusLine().getStatusCode()).isEqualTo(500);
}
}
It results in a file with the following content, excluding the matchingRules-block I would like to get in there:
{
"provider": {
"name": "foo-provider"
},
"consumer": {
"name": "foo-consumer"
},
"interactions": [
{
"description": "A server error",
"request": {
"method": "GET",
"path": "/foos"
},
"response": {
"status": "GET",
"body": {
"title": "Internal Server Error"
},
"matchingRules": {
"$.body.title": {
"match": "type"
}
}
}
}
]
}
Do you know how to include these matching rules in the file? Because, they are supported by the framework, and when added, let's say, manually, the provider-side test-implementation recognizes it and it works.
I use the following dependency/version:
<dependency>
<groupId>au.com.dius</groupId>
<artifactId>pact-jvm-consumer-junit5</version>
<version>4.0.10</version>
<scope>test</scope>
</dependency>
You get matching rules by using matchers. Currently, you're passing a hard coded JSON, so Pact doesn't know which bits should have matchers and which should be tested verbatim.
In your case, that would be something like this:
PactDslJsonBody body = new PactDslJsonBody()
.stringType("title", "Internal server error");
You can then pass it in like so:
#Pact(consumer = "foo-consumer")
RequestResponsePact createPact(PactDslWithProvider builder) {
return builder.given("A server error")
.uponReceiving("Get all Foos")
.path("/foos")
.willRespondWith()
.status(500)
.body(body)
.toPact()
}
I would like to deserialize the following json object with jackson:
{
"_embedded": {
"endpoints": [
{
"name": "Tester",
"id": "48aba1b3-3585-4327-a20f-627a1749611b",
"componentId": "Darwin2",
"_links": {
"self": {
"href": "www.google.com"
},
"network": {
"href": "www.google.com"
},
"appWans": {
"href": "www.google.com"
},
"services": {
"href": "www.google.com"
},
"endpointGroups": {
"href": "www.google.com"
},
"geoRegion": {
"href": "www.google.com"
},
"dataCenter": {
"href": "www.google.com"
}
}
}
]
},
"_links": {
"first": {
"href": "www.google.com"
},
"last": {
"href": "www.google.com"
}
},
"page": {
"size": 2000,
"totalElements": 1,
"totalPages": 1,
"number": 1
}
}
My goal is to implement an Embedded object then within this object add another object called Endpoints. Ideally, I'd be able to access the id property off of the endpoints object. However, I keep getting deserialization errors. For the moment I am using this class:
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonProperty;
#lombok.Value
public class Endpoints {
#JsonProperty("_embedded")
private Map<String, Object> embedded = new HashMap<>();
}
This at least affords me the opportunity to do the following:
Endpoints result = apiRequest.get();
if (result != null) System.out.println(result.getEmbedded().get("endpoints"));
Which prints out the array of endpoints, but I can't use this. I must implement a java object. Any help would be greatly appreciated with this issue.
So I have no idea what frameworks you use, or where this JSON data comes from, but you mentioned Spring HATEOS, so here is a solution using Jackson and Spring HATEOAS:
#Data
public class Endpoint extends RepresentationModel<Endpoint> {
private String name;
private String id; // Could be a UUID type instead??
private String componentId;
}
#Service
public class MyService {
#Autowired
private ObjectMapper objectMapper;
public void foo() {
String mysteryString = apiRequest.get();
PagedModel<Endpoint> endpointsPage = objectMapper.readValue(mysteryString, new TypeReference<PagedModel<Endpoint>>);
for (Endpoint e : endpointsPage) {
System.out.println(e.getName());
}
}
}
Spring HATEOAS docs and a guide. Also look at the Javadoc of the classes I've used.
If you want Endpoint to be a Lombok Value (i.e., all final), you need a constructor with appropriate ´#JsonCreator´ and ´#JsonProperty´ annotations, so Jackson knows how to build your Object (see 'guide' link).
And some more reading on Jackson.
Then again, if you are using Spring and you could just use Spring RestTemplate to fetch the Data from the remote API, you don't even need to manually use Jackson ObjectMapper:
PagedModel<Endpoint> endpointsPage =
restTemplate.exchange(apiUrl,
HttpMethod.GET,
null, // RequestEntity (body of request)
new ParameterizedTypeReference<PagedModel<Endpoint>>() {}).getBody();
The whole TypeReference and ParameterizedTypeReference business is only needed bc. we are dealing with generics.
{
"context": {
"headers": {},
"entity": {
"validationDetailsEntityList": [
{
"createTimestamp": 1512653225936,
"modifyTimestamp": 1512653225936,
"version": 0,
"auditTimestamp": "2"
},
{
"createTimestamp": 1512652876650,
"modifyTimestamp": 1512652876650,
"version": 0,
"auditTimestamp": "2"
},
{
"createTimestamp": 1512652955832,
"modifyTimestamp": 1512652955832,
"version": 0,
"auditTimestamp": "2"
}
]
}
"entityType":"com.example.demo.wrapper.ABCDWrapper",
"entityAnnotations": [],
Class Written below to get the response on the http mapped request
#RequestMapping(value = "/fetch", method = RequestMethod.POST)
public Response getAllXYZDetails(#RequestBody QueryDetails queryDetailsPayLoad) {
List<XYZEntity> xyzEntityList = xyzService.getAllXYZDetails();
return Response.ok(xyzEntityList)
.build();
}
I am trying to build a generic response type from my controller class on http REST calls, and so my return type is Response.
Now, what's happening is that:
The response generated not only has the details that I want in json but also it is having a lot of extra information like
entityType and entityAnnotations etc etc(SEE ABOVE RESPONSE), which I don't want.
How to get rid of those ans get only the entities in response?
If you are using Jackson annotations you can configure pretty much everything. In your case, #JsonIgnore should suffice.
If you are using JAX-RS/JAXB then Add #JsonIgnoreProperties(ignoreUnknown = true) on top of your class
I have several controllers that are automatically creating REST endpoints.
#RepositoryRestResource(collectionResourceRel = "books", path = "books")
public interface BooksRepository extends CrudRepository<Books, Integer> {
public Page<Books> findTopByNameOrderByFilenameDesc(String name);
}
When I visit: http://localhost:8080/Books
I get back:
{
"_embedded": {
"Books": [{
"id": ,
"filename": "Test123",
"name": "test123",
"_links": {
"self": {
"href": "http://localhost:8080/books/123"
},
"Books": {
"href": "http://localhost:8080/books/123"
}
}
}]
},
"_links": {
"self": {
"href": "http://localhost:8080/books"
},
"profile": {
"href": "http://localhost:8080/profile/books"
},
"search": {
"href": "http://localhost:8080/books/search"
},
"page": {
"size": 20,
"totalElements": 81,
"totalPages": 5,
"number": 0
}
}
}
When I create my own controller:
#Controller
#RequestMapping(value = "/CustomBooks")
public class CustomBooksController {
#Autowired
public CustomBookService customBookService;
#RequestMapping("/search")
#ResponseBody
public Page<Book> search(#RequestParam(value = "q", required = false) String query,
#PageableDefault(page = 0, size = 20) Pageable pageable) {
return customBookService.findAll();
}
}
I'll get a response back that looks nothing like the automatically generated controller response:
{
"content": [{
"filename": "Test123",
"name" : "test123"
}],
"totalPages": 5,
"totalElements": 81,
"size": 20,
"number": 0,
}
What do I need to do to make my response look like the automatically generated response? I want to keep it consistent, so I don't have to rewrite code for a different response. Should I be doing it a different way?
Edit: Found this: Enable HAL serialization in Spring Boot for custom controller method
But I don't understand what I need to change in my REST Controller to enable: PersistentEntityResourceAssembler. I've searched on Google for PersistentEntityResourceAssembler, but it keeps leading me back to similar pages without much of an example (or the example doesn't seem to work for me).
As #chrylis suggested you should replace your #Controller annotation with #RepositoryRestController for spring-data-rest to invoke it's ResourceProcessors for customizing the given resource.
For you resource to follow the HATEOAS specification (like your spring-data-rest BooksRepository) your method declaration return type should be like HttpEntity<PagedResources<Resource<Books>>>
For converting your Page object to PagedResources:
You need to autowire this object.
#Autowired
private PagedResourcesAssembler<Books> bookAssembler;
Your return statement should be like
return new ResponseEntity<>(bookAssembler.toResource(customBookService.findAll()), HttpStatus.OK);
These changes should help you to get a org.springframework.hateoas.Resources compliant response containing the "_embedded" and "_links" attribute.
For some reason, my Spring controller is returning different responses if I access it via a browser or via my MockMVC test class. Could someone help me spot why?
First the controller method:
#RequestMapping(value = APPLICATIONS_ROOT, method = GET)
public HttpEntity<ApplicationsListResource> listApplications(#PageableDefault(page = DEFAULT_START,
size = DEFAULT_HITS_PER_PAGE) Pageable pageable) {
Page<Application> applications = applicationRepository.findAll(pageable);
ApplicationsListResource applicationListResource = new ApplicationsListResource(applications, pageable);
return new ResponseEntity<ApplicationsListResource>(applicationListResource, HttpStatus.OK);
}
Obviously there's a few unknown classes in there. ApplicationListResource extends ResourceSupport and contains a list of ApplicationResource called applications. This ApplicationResource also extends ResourceSupport.
When I access the code via the browser, I'll get something along the lines of:
{
"_links": {
"self": {
"href": "http://localhost:10000/applications{?page,size,sort}",
"templated": true
}
},
"_embedded": {
"applications": [{
"displayname": "One",
"description": "My Test Application!",
"locations": ["http://foo.com"],
"_links": {
"self": { "href": "http://localhost:10000/applications/one" }
}
}, {
...
}]
},
"page": {
"size": 20,
"totalElements": 7,
"totalPages": 1,
"number": 0
}
}
Looks HATEOAS compliant to me. But when I go via a MockMVC request...
getMockMvc().perform(get(APPLICATIONS_ROOT)).andExpect(status().isOk()).andExpect(content().contentType(MediaTypes.HAL_JSON)).andExpect(jsonPath("$._embedded.applcations", hasSize(5))).andReturn();
The responses have no HATEOAS compliant elements in them so my tests fail on the jsonPath check:
{
"page" : 0,
"size" : 10,
"sort" : null,
"total" : 5,
"applications" : [ {
"name" : "one",
"version" : "1.0",
...
I've tried changing the ContentType on the GET request for the MockMVC method but it makes no difference. In the browser, I'm not setting any specific content type, headers etc.
I know the MockMVC class makes it HTTP requests with certain differences from the usual RestTemplate so perhaps it's something like this? Can anyone see anything obvious I am missing?
I will add additional code if needs be but it would have made the question even more long winded than it is currently.
Spring HATEOAS adds additional configuration for rendering hal properly, check this for details: http://docs.spring.io/spring-hateoas/docs/0.19.0.RELEASE/reference/html/#configuration.
In a nutshell it adds proper MixIns added by Jackson2HalModule and HalHandlerInstantiator to the ObjectMapper. It's all configured in HypermediaSupportBeanDefinitionRegistrar.java (https://github.com/spring-projects/spring-hateoas/blob/master/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java)
If you're using standalone mockMvc configuration you have to configure the ObjectMapper manually to mimic spring's behaviour. I ran into same problem and ended up adding following configuration to my tests:
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setMessageConverters(
new MappingJackson2HttpMessageConverter(configureObjectMapper()))
.build();
and
private ObjectMapper configureObjectMapper() {
return Jackson2ObjectMapperBuilder.json()
.modules(new Jackson2HalModule())
.handlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(
new DelegatingRelProvider(
OrderAwarePluginRegistry.create(Arrays.asList(
new EvoInflectorRelProvider(),
new AnnotationRelProvider()))),
null))
.build();
}