I'm using Hamcrest to unit test a REST API.
When I send a request, I often check for a 200 status code like this :
public void myTest() {
url = "route/to/my/rest/api/";
secured().when().get(url).then().statusCode(200);
}
But when I get a wrong code status, I only get an assertion error. Is there a way to automatically dump the response body (which contains the error) when the status code doesn't match ?
The secured() method :
public RequestSpecification secured() {
return given().header("Authorization", "Bearer " + getAuth());
}
As I mentioned in the comments I used the following
secured().when().post(url).then().log().ifValidationFails(LogDetail.BODY).statusCode(200);
You can find the source in the documentation
You can add a message to the assertion when test fails:
.statusCode(describedAs("The test fails because ...", is(200)))
Related
I'm currently trying to build a contract like this:
Contract.make {
description("""
Represents a successful scenario of getting an account
```
given:
account id
when:
api request for an account
then:
return account
```
""")
request {
method 'GET'
urlPath(regex("/api/v1/account/" + anyNumber()))
}
response {
status 200
body(
accountNumber: value(anyNumber())
)
headers {
contentType(applicationJson())
}
}
}
But getting an exception
cloud:spring-cloud-contract-maven-plugin:4.0.0:generateTests failed: java.util.regex.PatternSyntaxException: Illegal repetition near index 34
[ERROR] /api/v1/account/ClientDslProperty{
[ERROR] clientValue=-?(\d*\.\d+|\d+),
[ERROR] serverValue=-90366052}
[ERROR] ^
If reading the documentation here this should be possible.
If I use hardcoded values it works e.g.
Contract.make {
description("""
Represents a successful scenario of getting an account
```
given:
account id
when:
api request for an account
then:
return account
```
""")
request {
method 'GET'
url '/api/v1/account/1234'
}
response {
status 200
body("""
{
"accountNumber": "${value(1234)}"
}
""")
headers {
contentType(applicationJson())
}
}
}
Can someome help me out please? Thanks a lot in advance
Spring Cloud Contract is new to me, but reading the documentation (https://cloud.spring.io/spring-cloud-contract/reference/html/project-features.html#contract-dsl-regex) suggests that when you define the request with a regex, you should be specifying that it's for the consumer side. Also, anyNumber() doesn't return a simple string that can be concatenated into a regex pattern like you've done. I think you want something like this:
url value(consumer(regex('/api/v1/account/\d+')))
Then, to reference the number in your response section, you probably want something like:
body(
accountNumber: fromRequest().path(3)
)
(this is from the section of documentation that you linked to in your question)
I'm using the javax.validation in my objects and in my annotation I have the errors messages like this:
#NotNull(message = "This name can't be a null parameter")
private String name;
When I use the #Valid on my endpoint the java will check my validations and return a error 400 with a list of errors inside. And in my integration test I check the error like this:
#Test
public void saveUser() throws Exception{
User user = builder.newUser();
getMockMvc().perform(post(URL_USER)
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(user)))
.andExpect(status().isBadRequest());
}
But this test are incomplete, because I dont check the error message.I need a way to check my message "This name can't be a null parameter". I know... This message come in a array of errors in a parameter called defaultMessage I think. Someone can help me?
You can assign the returning message with andReturn() to MvcResult then you can get response as String. After, you can parse (I used org.json here) this returning string and get error message.
final MvcResult mvcResult = getMockMvc().perform(post(URL_USER)
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(user)))
.andExpect(status().isBadRequest())
.andReturn();
final String resultString = mvcResult.getResponse().getContentAsString();
final JSONObject jsonObject = new JSONObject(resultString);
System.out.println(jsonObject.get("message"))
I'm working on Lagom POC on sending POST request to Non lagom service with custom Header. In my case I'm trying to hit postman-echo to test the custom header.
However, it looks the headers are not set though I made code changes accordingly:
public CompletionStage<DsapAuthorizationResponse> hitAPI(AuthorizationRequest request) {
DsapWSRequest dsapWSRequest = new DsapWSRequest();
dsapWSRequest.username = request.username;
dsapWSRequest.password = request.password;
CompletionStage<DsapAuthorizationResponse> dsapresponse = dsapExternalService
.authenticate()
.handleRequestHeader(requestHeader -> {
requestHeader.withHeader("Authorization","Basic mncndsjna");
System.out.println("My Headers>>>>>>>> " + requestHeader);
return requestHeader;
})
.handleResponseHeader((responseHeader,b) -> {
System.out.println("RESPonse Header >>>>>>> : "+responseHeader);
return b;
})
.invoke(dsapWSRequest);
return dsapresponse;
}
In the above code header authorization is not set in the request. I am not getting this header in the echo which is mapped correctly in my POJO.
here is the complete code from my GitHub
https://github.com/sourabhsar/Lagom-Unmanaged-Service-Demo/tree/poc/lagom-request-response
I followed the steps mentioned here:
https://groups.google.com/forum/#!topic/lagom-framework/yvKmqvtZWFs
and also followed few other blogs/articles.
However so far I haven't found any blog which they are sending request to unmanaged external service with custom header. I'm not sure whats wrong in my code.
requestHeader.withHeader returns a new object with the added header, but the code you have written returns the original requestHeader object. In general, many Lagom APIs follow a principle of using immutable objects, with methods that return a new, modified instance, rather than changing the instance the method is called on.
Try this:
.handleRequestHeader(requestHeader -> {
RequestHeader modifiedRequestHeader =
requestHeader.withHeader("Authorization","Basic mncndsjna");
System.out.println("My Headers>>>>>>>> " + modifiedRequestHeader);
return modifiedRequestHeader;
})
I'm working on a basic Twilio web application using Java and the Spark Java framework. I'm trying to have the user enter a number as input after the initial prompt through a Gather verb and then process that input. So far, I am able to call my Twilio number and it responds with the initial prompt, but after I enter a number it goes to /handle-number and crashes because the request did not contain any params and it can't find the "Digits" param (params is empty when I print it).
I have tried to mimic the API call via the Postman Chrome extension to debug it, but I get a 500 internal server error.
EDIT: Here is a screenshot of the postman request : Postman screenshot
I am new to Java web applications, HTTP requests, and Twilio, so I am unfamiliar with much of this. I have gone thought the twiml documentation and tutorials and tried to follow along but my I'm definitely missing something in my implementation.
How do I properly pass the phone input to the processNumber Route? Any help is appreciated!
App.java
import static spark.Spark.*;
public class App {
public static void main (String[] args){
post("/receive-call", ReceiveCall.call);
post("/handle-number", ReceiveCall.processNumber);
}
}
ReceiveCall.java
import com.twilio.twiml.voice.Gather;
import com.twilio.twiml.voice.Say;
import com.twilio.twiml.*;
import spark.Route;
public class ReceiveCall {
public static Route call = (request, response) -> {
Say sayMessage = new Say.Builder("Hello! Please enter a number as input. Enter # when finished.").build();
Gather input = new Gather.Builder().timeout(3).say(sayMessage).action("/handle-number").build();
VoiceResponse twiml = new VoiceResponse.Builder().gather(input).build();
System.out.println(response.body());
return twiml.toXml();
};
public static Route processNumber = ((request, response) -> {
String digit = request.params("Digits");
//CRASHES HERE BECAUSE digit IS NULL
int number = Integer.parseInt(digit);
Say message = process(number);
VoiceResponse twiml = new VoiceResponse.Builder().say(message).build();
return twiml.toXml();
});
The reason of "digit IS NULL" is: you are using request.params(...), which is for path parameter.
What is "path parameter"?
"path parameter" means passing parameter as part of URL path, especially in RESTful style request.
For example, if you want to send an HTTP GET request to retrieve a book by its ISBN, the request URL could be: /books/9787121022982 or /books/9787101054491, where the ISBN parameter is passed as part of URL path (9787121022982 and 9787101054491). In Spark framework, the corresponding Route code would be:
get("/books/:isbn", (request, response) -> {
return "Book ISBN is: " + request.params(":isbn");
});
What is "query parameter"?
"query parameter" means passing parameter as part of URL queries (entities after the ? character in URL).
Take the previous book ISBN case for example, if you want to pass ISBN as query parameter, the HTTP GET URL would be: /books?isbn=9787121022982, and the corresponding Spark Route code is:
get("/books", (request, response) -> {
return "Book ISBN is: " + request.queryParams("isbn");
});
What is the best practice to pass data in POST request?
In your case, the /handle-number route accept POST request. For HTTP POST request, it's not a good practice to pass data as parameter in URL. Instead, you need to pass data as request body, and get data from body in Spark code:
post("/handle-number", (request, response) -> {
String body = request.body();
// extract ISBN from request body string
});
I got a java.lang.AssertionError when I was attempting to verify the href. The response body looks fine,
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[application/json;charset=UTF-8]}
Content type = application/json;charset=UTF-8
Body = {"itemName":"ThinkPad","links":[{"rel":"self","href":"http://localhost/items/001"}]}
Forwarded URL = null
Redirected URL = null
Cookies = []
but when called this sentence: andExpect(jsonPath("$.links[0].href", hasItem(endsWith("/items/001"))))
The error occured:
java.lang.AssertionError: JSON path "$.links[0].href"
Expected: a collection containing ""
but: was "http://localhost/items/001"
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.springframework.test.util.JsonPathExpectationsHelper.assertValue(JsonPathExpectationsHelper.java:74)
at org.springframework.test.web.servlet.result.JsonPathResultMatchers$1.match(JsonPathResultMatchers.java:86)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171)
at scnz.api.controller.ItemDispatchControllerTest.getCurrentStock(ItemDispatchControllerTest.java:62)
Here is the test code:
#Test
public void getCurrentStock() throws Exception {
Item item = new Item("001", "ThinkPad");
when(service.retrieve("001")).thenReturn(item);
mockMvc.perform(get("/items/001"))
.andDo(print())
.andExpect(jsonPath("$.itemName", is(item.getItemName())))
.andExpect(jsonPath("$.links[0].href", hasItem(endsWith("/items/001"))))
.andExpect(status().isOk());
Can anyone figure out where is wrong?
The actual result of jsonPath(...) for "$.links[0].href" is just a String as stated in the AssertionError.
The expected result for hasItem(...) is a collection as stated in the AssertionError.
In your case therefore just use endsWith(...) withouch hasItem(...). If your expression for jsonPath(...) returns a collection (e.g. via "$.links[*].href") instead of a single item you should use hasItem(...).