Spring Reactive test case - junit

I'm using spring reactive webclient to call APIs. However its test case is not going through onStatus block.
return webClient.get()
.uri(url)
.retrieve()
.onStatus(HttpStatus::isError, response -> response.bodyToMono(Map.class)
.flatMap(errorMap -> {
log.error();
}))
I have tried -
when(...).thenReturn(Mono.error(new BadRequestException()));
Mono<Bean> bean = service.getMethod(any());
StepVerifier.create(bean).expectErrorMessage("1").verify();

Related

Mocking WsClient in unit tests in play scala

Consider following snippet; I have used WSClient here for some api calls via DI.
#Singleton
class SampleService #Inject()(ws: WSClient) {
def get(id:Long): JsValue ={
val trailingURL = s"/$id".toString
val wsRequest = ws.url(baseURL+trailingURL).addQueryStringParameters("access_token" -> authToken).get()
val wsResponse = Await.result(wsRequest, Duration.Inf)
Json.toJson(wsResponse.body)
}
}
And I need to write unit test for get method. I'm doing the following thing
val mockedWS = mock[WSClient]
val sparrowService = new SurveySparrowService(mockedWS)
"get method" should {
"return a valid result with valid id" in {
val result = sparrowService.get(66405)
println(result)
assert(result.toString == `the result i'll get`)
}
}
But the mocking fails and i get a null pointer exception in following line=>
val wsRequest = ws.url(baseURL+trailingURL).addQueryStringParameters("access_token" -> authToken).get()
Also when i'm using Json.toJson(wsResponse.body) i'm getting extra \ with each parameter in whole response.
can anyone help me solving these two problems. Thanks.
There is play-mockws, which exists solely because mocking a WSClient manually is really tedious.
// simulation of a GET request to http://dns/url
val ws = MockWS {
case (GET, "http://dns/url") => Action { Ok("http response") }
}
await(ws.url("http://dns/url").get()).body == "http response"
Further explanation:
Mocking a class / trait simply creates you an instance of that type out of thin air. You cannot do anything with that instance in general, calling any method on it will simply return null. If your code under test calls methods of this object, you must stub those methods with answers (i.e. simply return a prepared value).
For WSClient, this means you must stub the url method since this will be called by any code doing HTTP requests. But this method returns a WSRequest. So, you must mock this also... Any call on this new mock needs to be stubbed too, or else it will end in a NPE again. This really gets complicated very soon, and you probably don't understand your test code too well anymore. That's why play-mockws was created which makes it very easy to reason about calls to HTTP services in your Play application.
BTW, you may also combine play-mockws with the SIRD - String Interpolation Router DSL, which makes it even easier to extract values out of the routes or query parameters if you need to:
val ws = MockWS {
case GET(p"/$id") if id == "66405" =>
Action {
Results.Ok("...")
}
}

How to unit test Spring IntegrationFlow?

I have been using Spring Integration DSL to implement some messaging processing flow.
How can I actually unit test a single IntegrationFlow, can anyone provide me with an example on how to unit test i.e. transform part of this bean:
#Bean
public IntegrationFlow transformMessage(){
return message -> message
.transform(new GenericTransformer<Message<String>, Message<String>>() {
#Override
public Message<String> transform(Message<String> message) {
MutableMessageHeaders headers =
new MutableMessageHeaders(message.getHeaders());
headers.put("Content-Type", "application/json");
headers.put("Accept", "application/json");
String payload = "Long message";
ObjectMapper mapper = new ObjectMapper();
HashMap<String, String> map = new HashMap<>();
map.put("payload", payload);
String jsonString = null;
try {
jsonInString = mapper.writeValueAsString(map);
} catch (JsonProcessingException e) {
logger.error("Error:" + e.getMessage());
}
Message<String> request = new GenericMessage<String>(jsonString
, headers);
return request;
}
})
.handle(makeHttpRequestToValidateAcdrMessage())
.enrichHeaders(h -> h.header("someHeader", "blah", true))
.channel("entrypoint");
}
How can I test it?
Regards!
Seems for me "unit testing" means check the behavior of the particular part of the system, some small component.
So, in your case it is about that new GenericTransformer.
so, just make it as a top-level component and perform tests against its isolated instances!
The integration tests can be performed against the target IntegrationFlow as well.
Each EIP-component in the flow definition is surrounded with
MessageChannels - input and output. Even if you don't declare .channel() there, the Framework build implicit DirrectChannel to wire endpoints to the flow.
Those implicit get the bean name like:
channelBeanName = flowNamePrefix + "channel" +
BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + channelNameIndex++;
So, since your IntegrationFlow is from Lambda, the input channel form the .transform() is just input of the flow - transformMessage.input.
The channel between .transform() and the next .handle() has bean name like: transformMessage.channel#0, because it will be a first implicit channel declaration.
The idea that you can #Autowired both of this channels to your test-case and add ChannelInterceptor to them before testing.
The ChannelInterceptor may play verificator role to be sure that you send to the transformer and receive from the a proper data as it is expected.
More info can be found here: https://github.com/spring-projects/spring-integration-java-dsl/issues/23
The same techniques described in the testing-samples project in the samples repo can be used here.
The send a message to channel transform.input and subscribe to entrypoint to get the result (or change it to a QueueChannel in your test case.
Example of DSL IntegrationFlows testing is on github.

Spring RESTful web-service returns 404 AFTER url is successfully called

I have a Spring MVC 4 app with Spring Security 4 and is deployed on Tomcat 8 running under jdk 1.8. The web-service has the controller defined as such:
#RequestMapping(value = "/build", method = RequestMethod.POST, produces = "application/json", headers =
{ "Accept=*/*", "Content-Type=*/*" })
public SubjectEntity build(#RequestBody SubjectImportDTO subjectImportDTO)
{
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User user = null;
if (principal instanceof User)
{
user = ((User) principal);
}
SubjectEntity entity = service.build(subjectImportDTO);
System.out.println("FINISH: build");
return entity;
}
I am getting a csrf token, I have that setup correctly. I know the url is getting called correctly because I can see that in the logs when I get there. The service on the back-end is running, data is correctly entered into the database, I correctly get the write object, and using the Jackson Mapper, the object 'SubjectEntity' should be translated into JSON and sent back to the requesting client. This web-service has been unit tested under the Spring Web Test framework, and it works great!
So, I am familiar with an HTTP 404 error in not finding a URL when the wrong parameters are passed in, or you're trying to do a POST when it's a GET, etc. So many reasons why we can get a 404 error ...
BUT ... IN THIS CASE ... We've already gotten to the URL, executed the code, and then it has the data it needs. Since the Controller says we have content-type / and it produces application/json, I don't know what else could be wrong?
Any ideas?
You should add #ResponseBodyto your method. without this, Spring mvc tries to find another handler method which can send a response.
NB: #RestController automatically add #ResponseBody on each method in a controller.

Returning JSON Errors from Asp.Net MVC 6 API

I'm trying out building a web API with MVC 6. But when one of my controller methods throws an error, the content of the response is a really nicely formatted HTML page that would be very informative were this an MVC app. But since this is an API, I'd rather have some JSON returned instead.
Note: My setup is super basic right now, just setting:
app.UseStaticFiles();
app.UseIdentity();
// Add MVC to the request pipeline.
app.UseMvc();
I want to set this up universally. Is there a "right/best" way to set this up in MVC 6 for an API?
Thanks...
One way to achieve your scenario is to write an ExceptionFilter and in that capture the necessary details and set the Result to be a JsonResult.
// Here I am creating an attribute so that you can use it on specific controllers/actions if you want to.
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception;
context.Result = new JsonResult(/*Your POCO type having necessary details*/)
{
StatusCode = (int)HttpStatusCode.InternalServerError
};
}
}
You can add this exception filter to be applicable to all controllers.
Example:
app.UseServices(services =>
{
services.AddMvc();
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new CustomExceptionFilterAttribute());
});
.....
}
Note that this solution does not cover all scenarios...for example, when an exception is thrown while writing the response by a formatter.

Junit test for spring ws endpoint interceptor

Would appreciate any code examples of how to call a SpringWS endpoint intrceptor from a Junit test class. Particularly on how to prepare a SOAP message context and endpoint object. The SOAP message in the context will need to have a custom SOAP header included.
Something like....
public class MyInterceptorTest
private static String "... my XML SOAP test message ...";
#Test
public testMyInterceptor() {
myMessageContext = ... Build a MessageContext with the XML message string;
myEndPointObject = ... Build an endpoint object;
boolean result = MyInterceptorClass.handleRequest(myMessageContext, myEndPointObject);
... Check results;
}
Any examples would be appreciated.
The MessageContext can be created by instantiating a DefaultMessageContext object. The request WebServiceMessage can created using the test support class PayloadMessageCreator, but this only appeared in Spring-WS 2.x.
The endpoint object can be anything - it depends what your interceptor does with it. If it doesn't actually use it, then you can just pass in null.
I had the same issue and was able to figure it out in part using #skaffman's suggestion.
Basically, I had a custom EndpointInterceptor that I wanted to test with real data so that I would know I had everything correct.
You will have to upgrade spring-ws-test and other spring-ws dependencies to version 2.0 or higher. I ended up using something different than PayloadMessageCreator.
final Source payload = new StreamSource(new StringReader(soapPayload));
SaajSoapMessageFactory saajSoapMessageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());
WebServiceMessage requestPayload = new SoapEnvelopeMessageCreator(payload).createMessage(saajSoapMessageFactory);
MessageContext messageContext = new DefaultMessageContext(requestPayload, saajSoapMessageFactory);
soapPayload is the string value of an entire soap envelope.
Something similar to this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
...fill in your custom headers here
</soapenv:Header>
<soapenv:Body><someRequest>...</someRequest></soapenv:Body>
</soapenv:Envelope>
You will obviously need to fill in your request payload, any namespaces, as well as your custom headers.
I set the endpoint object to null as I was not doing anything with it as part of my interceptor.