Using service in Pact consumer test with Java EE - pact

I'd like to implement a Pact consumer test in our Java EE application. This test shall invoke a consumer service method which would trigger the actual REST call.
Here's the Pact test so far:
#ExtendWith(PactConsumerTestExt.class)
#PactTestFor(providerName = "my-service")
public class MyServiceConsumerTest {
#Inject
private MyService myService;
#Pact(consumer = "myConsumer")
public RequestResponsePact mail(PactDslWithProvider builder) {
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", ContentType.getJSON().asString());
PactDslJsonBody jsonBody = new PactDslJsonBody()
.stringValue("emailAddress", "foo#bar.com")
.stringValue("subject", "Test subject")
.stringValue("content", "Test content")
.asBody();
return builder
.given("DEFAULT_STATE")
.uponReceiving("request for sending a mail")
.path("/mail")
.method("POST")
.headers(headers)
.body(jsonBody)
.willRespondWith()
.status(Response.Status.OK.getStatusCode())
.toPact();
}
#Test
#PactTestFor(pactMethod = "mail")
public void sendMail() {
MailNotification mailNotification = MailNotification.builder()
.emailAddress("foo#bar.com")
.subject("Test subject")
.content("Test content")
.build();
myService.sendNotification(mailNotification);
}
}
The interesting part is this line:
myService.sendNotification(mailNotification);
As I'm running a consumer unit test, the injection of MyService does not work, i.e. results in myService being null. Moreover I think it would be necessary to tell the service to send its request against the Pact mock serveR?
Of course I could just fire the final REST request in the test but that would ignore the service logic.
I guess I'm missing something here?

Yes, you should hit the mock server in the #PactVerification test. Don't fire without the actual application code, it makes a few sense in case of future changes. Tests should fail if you change an HTTP property of that request

Related

Mock IRequestClient<> during Integration Testing using MassTransit

I'm trying to do integration testing against a MediatR Command whose handler depends on an IRequestClient injected into its constructor.
public class SayHelloCommand : IRequest<string>
{
}
public class SayHelloCommandHandler : IRequestHandler<SayHelloCommand, string>
{
private readonly IRequestClient<IGetProfileMessageResult> _profileClient;
public SayHelloCommandHandler(IRequestClient<IGetProfileMessageResult> profileClient)
{
_profileClient = profileClient;
}
public async Task<string> Handle(SayHelloCommand request, CancellationToken cancellationToken)
{
var profile = (await _profileClient.GetResponse<IGetProfileMessageResult>(new {ProfileId = 1})).Message;
return $"Hello {profile.FirstName}";
}
}
I've setup my test suite to use the InMemoryMassTransit but whenever I run my test it times out when it reaches the call using the IRequestClient<>. I've also tried to moq the IRequestClient to return a default response like this -
[Test]
public async Task ShouldSayHello()
{
var mockRequestClient = new Mock<IRequestClient<IGetProfileMessageResult>>();
mockRequestClient.Setup(x => x.GetResponse<IGetProfileMessageResult>(It.IsAny<Object>(), default, default)
.Result.Message).Returns(new GetProfileMessageResult
{
FirstName = "John"
});
serviceCollection.Add(new ServiceDescriptor(typeof(IRequestClient<IGetProfileMessageResult>), mockRequestClient.Object));
var result = await SendAsync(command);
result.Status.Should().BeFalse();
result.Message.Should().Contain("John");
}
but this still times out.
Is there a way I can set up the InMemoryMassTransit to return a default response when the requestclient is called?
You could use the in-memory test harness to setup a simple consumer that would respond to the request, instead of trying to mock IRequestClient. Though you should be able to mock it if you want, I just don’t know the syntax to properly configure your mock framework.
There are many samples using the test harness available, as well as all of the MassTransit unit tests.

Setting custom date formats through Jackson2ObjectMapperBuilder causing request processing to continue after exception

I have a MockMvc test for testing that a JSON payload to a controller is validated and a HTTP 400 (bad request) is rendered for org.springframework.data.mapping.PropertyReferenceException and org.springframework.http.converter.HttpMessageConversionException.
The respective exception handlers are implemented as follows.
#ControllerAdvice
public class LocalExceptionHandler {
#ExceptionHandler(PropertyReferenceException.class)
public ResponseEntity<Object> handlePropertyReferenceException(PropertyReferenceException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
#ExceptionHandler(HttpMessageConversionException.class)
public ResponseEntity<Object> handleHttpMessageConversionException(HttpMessageConversionException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
I'm using a Spock specification for implementing the test. The setup is as follows.
MockMvc mvc
public JsonSerializer[] buildJsonSerializers() {
return new JsonSerializer[]{new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)),
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT))};
}
Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> {
builder.simpleDateFormat(DATE_TIME_FORMAT);
builder.serializers(buildJsonSerializers());
};
}
protected ObjectMapper buildObjectMapper() {
def objectMapperBuilder = new Jackson2ObjectMapperBuilder()
jsonCustomizer().customize(objectMapperBuilder)
objectMapperBuilder.modules(new MoneyModule()
.withMonetaryAmount(Money::of)
.withAmountFieldName("number")
.withFormattedFieldName("pretty"))
objectMapperBuilder.build()
}
def setup() {
ObjectMapper mapper = buildObjectMapper()
def mockMvcBuilder = MockMvcBuilders
.standaloneSetup(controller)
.setControllerAdvice(LocalExceptionHandler.class)
.setMessageConverters([new MappingJackson2HttpMessageConverter(mapper)]
.toArray(new HttpMessageConverter[1]))
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
mvc = mockMvcBuilder.build()
}
So the above setup just sets the date format through a customizer and then builds the object mapper using the Jackson2ObjectMapperBuilder.
The problem with that setup is that the builder is causing an object mapper configuration that results in a weird MockMvc behaviour.
When posting a bad request to a controller, a proper Exception is thrown and handled by one of the above exception handlers but request processing is not stopped and the controller method is invoked.
When running the production code (as Spring Boot application) error handling is just fine resulting with a HTTP 400.
Just by removing the builder and mimicking just the configuration desired for the test (which is a proper date time format) the test works as expected and request processing is stopped after exception handling.
So basically instead of using the builder I do
def mapper = new ObjectMapper()
mapper.registerModule(new MoneyModule()
.withMonetaryAmount(Money::of)
.withAmountFieldName("number")
.withFormattedFieldName("pretty"))
SimpleModule serializerModule = new SimpleModule()
Arrays.asList(buildJsonSerializers())
.forEach({ s -> serializerModule.addSerializer(s.handledType(), s) })
mapper.registerModule(serializerModule)
So it really looks like the builder is adding some configuration that MockMvc doesn't really deal with properly.
Would appreciate hints on resolving this.

How to start with PACT contract testing in java for a newbie

I have to do a POC on contract testing using pact, but I couldn't found anything helpful for a newbie. Can someone help me with the working code, how to install, execute I will be grateful.
I tried to explain below.
Consumer: Contract created by consumer.
Provider: Contracts tested by provider.
Pack Broker: After contracts are created under location (like targer/pacts) defined by you, you must publish the contracts to the common platform where consumer and provider will see.
Consumer side - Create contract for provider
public class CreateContractForProvider {
#Rule //Provider, HostInterface and Port defined with #Rule annotation (Used PactProviderRuleMk2)
public PactProviderRuleMk2 pactProviderRuleMk2 = new PactProviderRuleMk2(
// Provider Application Name
"ProviderName",
//Mock Server
"localhost",
8112,
this);
#Pact(consumer = "ConsumerName") // Consumer Application Name (Our application) - Consumer defined with #Pact annotation(
public RequestResponsePact createPact(PactDslWithProvider builder) {
Map<String, String> headers = new HashMap();
headers.put("Content-Type", "application/json"); //Defined headers
//Defined responses with PactDslJsonBody()
DslPart expectedResultBodyWhenGetPayments = new PactDslJsonBody()
.integerType("id",308545)
.integerType("contractNo",854452)
.numberType("amount",3312.5)
.stringType("status","UNPAID")
.asBody();
return builder
.uponReceiving("A request for all payments")
.path("/payments")
.method("GET")
.willRespondWith()
.status(200)
.headers(headers)
.body(expectedResultBodyWhenGetPayments).toPact(); //Response bodyies and headers used in return builder
// We can define more than one endpoint with .uponReceiving or .given
//Then we have to test beacuse contracts are created test stage.
//When we say test with #PactVerification, the server we described above stands up(localhost:8112). İf we get localhost:8112/(definedpathlike/payments) its return expectedResultBodyWhenGetPayments.If the test is successful, the contracts is create.
#Test
#PactVerification()
public void pactVerification() {
int contractNo=((Integer) new ContractTestUtil(pactProviderRuleMk2.getPort()).getContractResponse("/payments","contractNo")).intValue();
assertTrue(contractNo == 854452);
}}
Test Util
public class ContractTestUtil {
int port=8111;
public ContractTestUtil(int port) {
this.port=port;
System.out.println("Custom port "+port);
}
public Object getContractResponse(String path,String object) {
try {
System.setProperty("pact.rootDir", "./target/pacts");
System.setProperty("pact.rootDir", "./target/pacts");
String url=String.format("Http://localhost:%d"+path, port);
System.out.println("using url: "+url);
HttpResponse httpResponse = Request.Get(url).execute().returnResponse();
String json = EntityUtils.toString(httpResponse.getEntity());
System.out.println("json="+json);
JSONObject jsonObject = new JSONObject(json);
return jsonObject.get(object);
}
catch (Exception e) {
System.out.println("Unable to get object="+e);
return null;
}
}}
Define Pact Broker
The PactBrokerUr lmust be defined before publishing in pom.
<plugin>
<!-- mvn pact:publish -->
<groupId>au.com.dius</groupId>
<artifactId>pact-jvm-provider-maven_2.11</artifactId>
<version>3.5.10</version>
<configuration>
<pactDirectory>./target/pacts</pactDirectory> <!-- Defaults to ${project.build.directory}/pacts -->
<pactBrokerUrl>http://yourmachine:8113</pactBrokerUrl>
<projectVersion>1.1</projectVersion> <!-- Defaults to ${project.version} -->
<trimSnapshot>true</trimSnapshot> <!-- Defaults to false -->
</configuration>
</plugin>
Now, we can publish with pact:puplish command.
Provider Side - Call contracts created by consumer
In this stage you can test with failsafe plugin. Beacuse its integraion test.
#RunWith(PactRunner.class) // Say JUnit to run tests with custom Runner
#Provider("ProviderName")
#Consumer("ConsumerName")// Set up name of tested provider// Provider Application Name
#PactBroker(port = "8113", host = "yourmachine")
public class VerifyContractsWhichCreatedForProviderIT {
private static ConfigurableWebApplicationContext configurableWebApplicationContext;
#BeforeClass
public static void start() {
configurableWebApplicationContext = (ConfigurableWebApplicationContext)
SpringApplication.run(Application.class);
}
#TestTarget // Annotation denotes Target that will be used for tests
public final Target target = new HttpTarget(8080); //Test Target
}
Finally,you can create contrats and verify contrast created for you with clean test pact:publish verify command.

SpringWebMvcTest - Test Requestbody using #Valid and custom validation

I am trying to test my controller endpoint and my requestbody annotated with #Valid annotation. My Testclass looks like the follow:
#RunWith(SpringRunner.class)
#WebMvcTest(value = BalanceInquiryController.class, secure = false)
public class BalanceInquiryControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private BalanceInquiryController balanceInquiryController;
#Test
public void testGetBalanceInquiry() throws Exception {
RequestBuilder requestBuilder = MockMvcRequestBuilders
.post("/com/balanceInquiry")
.accept(MediaType.APPLICATION_JSON)
.content("{\"comGiftCard\":{\"cardNumber\":\"1234567890\",\"pinNumber\":\"0123\"},\"comMerchant\":\"MERCHANT1\"}")
.contentType(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
assertEquals(HttpStatus.OK.value(), response.getStatus());
}
}
My Controller - #PostMapping looks like that:
#PostMapping(value = "/com/balanceInquiry")
public ResponseEntity<?> getBalanceInquiry(#Valid #RequestBody BalanceInquiryModel balanceInquiry, Errors errors) {
if (errors.hasErrors()) {
return new ResponseEntity<String>("Validation error", HttpStatus.BAD_REQUEST);
}
//do any stuff...
return new ResponseEntity<BalanceInquiryResponse>(balanceInquiryResponse, HttpStatus.OK);
}
My BalanceInquiryModel is annotated with #Valid and has some hibernate and custom validations behind. Those validations are all ok and already unit tested.
What I like to test is my endpoint where I send a valid json request body expecting a 200 response and also an invalid json request body expecting a 400 response validated by the set #Valid implementation.
For example an unvalid call is to send no pinNumber or length < 4.
I have read some threads and some uses MockMvcBuilders.standaloneSetup() to mock the full controller. But I wont do a full integration test.
Not quite sure how to go on with this situation and if I should go on.
P.S.: At the moment I get always a 200 response no matter if the validation should give an error or not.
Here a gist for more code and the validation classes/models.
Here's one of my example I work on my project
hope it help you out:
I have a global exception handler to handler my MethodArgumentNotValidException and throw it
#RequestMapping(value = "/add", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> createUser(#Valid #RequestBody User user) {
User savedUser = userService.save(user);
return new ResponseEntity<User>(savedUser, HttpStatus.CREATED);
}
public void testAdduser() throws Exception{
final User request = new User();
request.setFirstName("Test");
request.setLastName("some description");
mockMvc.perform(post(END_POINT+"/add")
.contentType(MediaType.APPLICATION_JSON)
.content(stringify(request))
).andDo(print()).andExpect(status().isUnprocessableEntity())
;
}
private String stringify(Object object) throws JsonProcessingException {
return new ObjectMapper().writeValueAsString(object);
}
Update:
I think your main problem is that you are using #WebMvcTest in stead of #SpringBootTest.
the different between 2 of them is that:
#SpringBootTest annotation will loads complete application and injects all the beans which is can be slow.
#WebMvcTest - for testing the controller layer. it doesn't inject other bean beside the #RestController
so if you are just testing just pure controller to see u can reach the endpont then you can just use #WebMvcTest which will make your test run faster.
but in your case, you want it to run the spring validation, you will need to use #SpringBootTest
for detailed: https://spring.io/guides/gs/testing-web/

Spring Cloud Feign with OAuth2RestTemplate

I'm trying to implement Feign Clients to get my user info from the user's service, currently I'm requesting with oAuth2RestTemplate, it works. But now I wish to change to Feign, but I'm getting error code 401 probably because it doesn't carry the user tokens, so there is a way to customize, if Spring support for Feign is using, a RestTemplate so I can use my own Bean?
Today I'm implementing in this way
The service the client
#Retryable({RestClientException.class, TimeoutException.class, InterruptedException.class})
#HystrixCommand(fallbackMethod = "getFallback")
public Promise<ResponseEntity<UserProtos.User>> get() {
logger.debug("Requiring discovery of user");
Promise<ResponseEntity<UserProtos.User>> promise = Broadcaster.<ResponseEntity<UserProtos.User>>create(reactorEnv, DISPATCHER)
.observe(Promises::success)
.observeError(Exception.class, (o, e) -> Promises.error(reactorEnv, ERROR_DISPATCHER, e))
.filter(entity -> entity.getStatusCode().is2xxSuccessful())
.next();
promise.onNext(this.client.getUserInfo());
return promise;
}
And the client
#FeignClient("account")
public interface UserInfoClient {
#RequestMapping(value = "/uaa/user",consumes = MediaTypes.PROTOBUF,method = RequestMethod.GET)
ResponseEntity<UserProtos.User> getUserInfo();
}
Feign doesn't use a RestTemplate so you'd have to find a different way. If you create a #Bean of type feign.RequestInterceptor it will be applied to all requests, so maybe one of those with an OAuth2RestTemplate in it (just to manage the token acquisition) would be the best option.
this is my solution, just to complement the another answer with the source code, implementing the interface feign.RequestInterceptor
#Bean
public RequestInterceptor requestTokenBearerInterceptor() {
return new RequestInterceptor() {
#Override
public void apply(RequestTemplate requestTemplate) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)
SecurityContextHolder.getContext().getAuthentication().getDetails();
requestTemplate.header("Authorization", "bearer " + details.getTokenValue());
}
};
}

Resources