How to unit test Spring Integration flow - specifically http outbound gateway - unit-testing

I wonder if anyone can help - this has been driving me crazy for days!
I have a fairly simple Spring Integration file that I'd like to unit test. The SI uses an http outbound gateway, and I specifically want to unit test rather than integration test - I do not want to provide a mock http server using something like Spark or MockRestServiceServer.
My SI config looks like this:
<int:channel id="modifiedAttractionChannel" datatype="u.o.n.p.i.a.s.AttractionUuid">
<int:interceptors>
<int:wire-tap channel="attractionModifiedChannelLogging"/>
</int:interceptors>
</int:channel>
<int-http:outbound-gateway
id="serviceGateway"
request-channel="modifiedAttractionChannel"
url="/attractions/{uuid}"
http-method="GET"
expected-response-type="u.o.n.p.i.a.v.m.Attraction"
charset="UTF-8"
reply-timeout="${vader.reply.timeout}"
request-factory="clientHttpRequestFactory"
reply-channel="vaderAttractionChannel">
<int-http:uri-variable name="uuid" expression="headers['#{T(u.o.n.p.i.a.s.AttractionsImportInitializer).HEADER_UUID}'].value" />
</int-http:outbound-gateway>
<int:channel id="attractionChannel" datatype="u.o.n.p.i.a.v.m.Attraction">
<int:interceptors>
<int:wire-tap channel="vaderAttractionChannelLogging"/>
</int:interceptors>
</int:channel>
<int:logging-channel-adapter
id="attractionModifiedChannelLogging"
expression="'attractionModifiedChannel: header=' + headers['#{T(u.o.n.p.i.a.s.AttractionsImportInitializer).HEADER_UUID}'].value + ', payload=' + payload"
level="INFO" />
<int:logging-channel-adapter
id="vaderAttractionChannelLogging"
expression="'attractionModifiedChannel: header=' + headers['#{T(u.o.n.p.i.a.s.AttractionsImportInitializer).HEADER_UUID}'].value + ', payload=' + payload"
level="INFO" />
I have written a unit test that wires up a basic spring context and am able to get the modifiedAttractionChannel and send an appropriately built Message with an AttractionUuid payload and header value.
My unit test can assert that the log message written to attractionModifiedChannelLogging is as I expect it (I created the AttractionUuid and Message, so I know the payload and header values)
What I now need to do is assert the value written to the vaderAttractionChannelLogging wiretap. IE. I need to assert a message with a given header value - no problem, I created the header value as part of the test - but also the payload.
In this case the payload is the output of the outbound gateway. Given that this is a unit test and I don't want any dependency on anything else, I have provided a mock ClientHttpRequestFactory which in turn provides a mock ClientHttpResponse via a mock ClientHttpRequest
This works great in that I can control the response body that the outbound gateway would otherwise receive. However, the RestTemplate calls its HttpRequestExecutingMessageHandler in order to convert the response body into an object via the MessageConverters
Whilst this works in respect of the execution of the SI flow, it means the instance of Attraction that is on the reply-channel vaderAttractionChannel is not in control of the test; and therefore I cannot make any assertions about it in respect of what gets logged on the vaderAttractionChannelLogging wiretap.
I think one way of addressing this is to be able to wire in a mock or stub MessageConvertor instead of the standard set that returns a fixed Attraction instance. But I can't for the life of me work out how to!
Note: The scenario above is a much simplified version of what I'm actually trying to do. I'm not really trying to write unit tests around logged values! I need to test the overall flow of my SI, and being able to control the instance of the Attraction that the outbound gateway returns is very much key to that.
Any help with this would be very much appreciated;
Cheers
Nathan

It's not clear what your issue is; "...the RestTemplate calls its HttpRequestExecutingMessageHandler..." - it's actually the other way around. If you really want to unit test the flow, you should provide a normal result from the mock that will be converted by the standard converters. If you really want to mock the conversion too, use the message-converters attribute.

Related

Accessing request container (event_dispatcher) within a test client

I created a simple test case in Symfony.
So one client which should listen for an event which will be dispatched during an request.
But nothing happen because the request have an own scope or I dont know why Im not able to access the dispatcher in it.
$this->client = static::createClient();
self::$container = $this->client->getContainer();
$dispatcher = self::$container->get('event_dispatcher');
$dispatcher->addListener('example', function ($event) {
// Never executed
});
$this->client->request('POST', $endpoint, $this->getNextRequestParameters($i), [$file], $this->requestHeaders);
$this->client->getResponse();
The listener is never called.
When I debug it a bit I find out that the object hash via spl_object_hash($dispatcher) is different on the highest level than on within the request level.
So it seems that the request has an own world and ignores everything outside.
But then is the question how I can put my listener to this "world"?
I think part of the problem is the mixing of testing styles. You have a WebTestCase which is intended for a very high level of testing (requests & responses). It should not really care about internals, i.e. which services or listeners are called. It only cares that given input x (your request) you will get output y (your response). This allows to ensure the basic functionality as perceived by your users is always met, without caring how it is done. Making these tests very flexible.
By looking into the container and the services you are going into a lower level of testing, which tests interconnected services. This is usually only done within the same process for the reasons you already found out. The higher level test has 2 separate lifecycles, one for the test itself and one for the simulated web request to your application, hence the different object ids.
The solution is either to emit something to the higher level, e.g. by setting headers or changing the output, so you can inspect the response body. You could also write into some log file and check the logs before/after the request for that message.
A different option would be to move the whole test into a lower level where you do not need the requests and instead only work with the services. For this you can use the KernelTestCase (instead of the WebTestCase) and instead of calling createClient() you call bootKernel. This will give you access to your container where you can modify the EventDispatcher. Rather than sending a request you can then either call the code directly, e.g. dispatch an event if you only want to test the listeners, or you can make your controller accessible as service and then manually create a request, call the action and then either check the response or whatever else you want to assert on. This could look roughly like this:
public function testActionFiresEvent()
{
$kernel = static::bootKernel();
$eventDispatcher = $kernel->getContainer()->get('event_dispatcher');
// ...
$request = Request::create();
// This might not work when the controller
// You can create a service configuration only used by tests,
// e.g. "config/services_test.yaml" and provide the controller service there
$controller = $kernel->getContainer()->get(MyController::class);
$response = $controller->endpointAction($request);
// ...Do assertions...
}

Logic need to checked in Unit test cases method

I have a GET method for webAPI which returns say hundred Products list. What logic should one need to check to diagnose the test as pass or fail?
Should I check for count>0 or anything else?
Ideally I should not check for product count as it may change (count==100).
Check out these useful links on unit testing with async web requests:
https://codereview.stackexchange.com/questions/85321/unit-testing-http-requests
https://msdn.microsoft.com/en-us/library/hh404088.aspx
https://codeutopia.net/blog/2015/01/30/how-to-unit-test-nodejs-http-requests/
http://www.jeremyg.net/entry/unit-testing-a-fetch-api-service-in-aurelia
http://lazamar.github.io/testing-http-requests-with-jasmine/
Why should I mock HTTP requests inside unit tests?
Suppose your client component has a variable count that it initialized to 0. Then you fire some web request, and it responds with data like this:
{
response: 500
}
where response can have any whole number value. Then count gets set to the value of response.
The basic gist of this unit test would be to mock the actual calling to the server (instead of making the api call and return the response, just return a hardcoded object). Then assert that the "count" variable is as you would expect it to be from this predefined response. You can then set up multiple cases (ie multiple tests) for each possible type of response that can be returned. Good luck!

Unit test for web service (Service Reference) - xml deserialization

In Summary
I need a way to deserialize an XML string into an object normally returned by a 3rd party webservice.
Using C#.
In Detail
I have code that consumes a 3rd party Service Reference (Web Service) - so the usual stuff: we pass in a Request object and it returns a Response object.
Regarding unit testing - I'm not interested in the inner workings of the Service Reference since this is a 3rd party service. I'm only interested in two things:
Does my code generate the correct Request object?
When the Service Reference returns it's response, do I process this response correctly?
Taking each in turn:
Does my code generate the correct Request object?
This I can do. If anyone's interested in this, what I do is to replace my service reference with a RhinoMocks Mock object. In my unit test I call the method on my Mock and then check the arguments passed in, comparing the actual Request object against the expected Request object.
When the Service Reference returns it's response, do I process this response correctly?
What I want to do here is to create a RhinoMocks Stub of my service reference, so that when it's called this stub returns a response object populated with my test data.
The problem that I face is that the response objects returned by this particular 3rd party service are extremely complex. If I were to attempt to create one by hard-coding all the property values by hand then this would probably take me the best part of a whole day.
However, what I can very easily do is to capture the XML serialized response from this service. I could then easily edit it's values and store this XML in one of my unit tests.
What I'm after is an easy way to then "deserialize" this "test" XML into a response object and use this to program the response from my Stub.
Any help would be much appreciated.
Thanks
Griff
Turns out that this is quite simple:
public static object Deserialize(string xml, Type toType)
{
using(Stream stream = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
stream.Write(data, 0, data.Length);
stream.Position = 0;
var s = new XmlSerializer(toType, "http://^your url^");
return s.Deserialize(stream);
}
}
Note that if you're using XML from a SOAP request, strip the SOAP envelop off first.
Griff

How are integration tests written for interacting with external API?

First up, where my knowledge is at:
Unit Tests are those which test a small piece of code (single methods, mostly).
Integration Tests are those which test the interaction between multiple areas of code (which hopefully already have their own Unit Tests). Sometimes, parts of the code under test requires other code to act in a particular way. This is where Mocks & Stubs come in. So, we mock/stub out a part of the code to perform very specifically. This allows our Integration Test to run predictably without side effects.
All tests should be able to be run stand-alone without data sharing. If data sharing is necessary, this is a sign the system isn't decoupled enough.
Next up, the situation I am facing:
When interacting with an external API (specifically, a RESTful API that will modify live data with a POST request), I understand we can (should?) mock out the interaction with that API (more eloquently stated in this answer) for an Integration Test. I also understand we can Unit Test the individual components of interacting with that API (constructing the request, parsing the result, throwing errors, etc). What I don't get is how to actually go about this.
So, finally: My question(s).
How do I test my interaction with an external API that has side effects?
A perfect example is Google's Content API for shopping. To be able to perform the task at hand, it requires a decent amount of prep work, then performing the actual request, then analysing the return value. Some of this is without any 'sandbox' environment.
The code to do this generally has quite a few layers of abstraction, something like:
<?php
class Request
{
public function setUrl(..){ /* ... */ }
public function setData(..){ /* ... */ }
public function setHeaders(..){ /* ... */ }
public function execute(..){
// Do some CURL request or some-such
}
public function wasSuccessful(){
// some test to see if the CURL request was successful
}
}
class GoogleAPIRequest
{
private $request;
abstract protected function getUrl();
abstract protected function getData();
public function __construct() {
$this->request = new Request();
$this->request->setUrl($this->getUrl());
$this->request->setData($this->getData());
$this->request->setHeaders($this->getHeaders());
}
public function doRequest() {
$this->request->execute();
}
public function wasSuccessful() {
return ($this->request->wasSuccessful() && $this->parseResult());
}
private function parseResult() {
// return false when result can't be parsed
}
protected function getHeaders() {
// return some GoogleAPI specific headers
}
}
class CreateSubAccountRequest extends GoogleAPIRequest
{
private $dataObject;
public function __construct($dataObject) {
parent::__construct();
$this->dataObject = $dataObject;
}
protected function getUrl() {
return "http://...";
}
protected function getData() {
return $this->dataObject->getSomeValue();
}
}
class aTest
{
public function testTheRequest() {
$dataObject = getSomeDataObject(..);
$request = new CreateSubAccountRequest($dataObject);
$request->doRequest();
$this->assertTrue($request->wasSuccessful());
}
}
?>
Note: This is a PHP5 / PHPUnit example
Given that testTheRequest is the method called by the test suite, the example will execute a live request.
Now, this live request will (hopefully, provided everything went well) do a POST request that has the side effect of altering live data.
Is this acceptable? What alternatives do I have? I can't see a way to mock out the Request object for the test. And even if I did, it would mean setting up results / entry points for every possible code path that Google's API accepts (which in this case would have to be found by trial and error), but would allow me the use of fixtures.
A further extension is when certain requests rely on certain data being Live already. Using the Google Content API as an example again, to add a Data Feed to a Sub Account, the Sub Account must already exist.
One approach I can think of is the following steps;
In testCreateAccount
Create a sub-account
Assert the sub-account was created
Delete the sub-account
Have testCreateDataFeed depend on testCreateAccount not having any errors
In testCreateDataFeed, create a new account
Create the data feed
Assert the data feed was created
Delete the data feed
Delete the sub-account
This then raises the further question; how do I test the deletion of accounts / data feeds? testCreateDataFeed feels dirty to me - What if creating the data feed fails? The test fails, therefore the sub-account is never deleted... I can't test deletion without creation, so do I write another test (testDeleteAccount) that relies on testCreateAccount before creating then deleting an account of its own (since data shouldn't be shared between tests).
In Summary
How do I test interacting with an external API that effects live data?
How can I mock / stub objects in an Integration test when they're hidden behind layers of abstraction?
What do I do when a test fails and the live data is left in an inconsistent state?
How in code do I actually go about doing all this?
Related:
How can mocking external services improve unit tests?
Writing unit tests for a REST-ful API
This is more an additional answer to the one already given:
Looking through your code, the class GoogleAPIRequest has a hard-encoded dependency of class Request. This prevents you from testing it independently from the request class, so you can't mock the request.
You need to make the request injectable, so you can change it to a mock while testing. That done, no real API HTTP requests are send, the live data is not changed and you can test much quicker.
I've recently had to update a library because the api it connects to was updated.
My knowledge isn't enough to explain in detail, but i learnt a great deal from looking at the code. https://github.com/gridiron-guru/FantasyDataAPI
You can submit a request as you would normally to the api and then save that response as a json file, you can then use that as a mock.
Have a look at the tests in this library which connects to an api using Guzzle.
It mocks responses from the api, there's a good deal of information in the docs on how the testing works it might give you an idea of how to go about it.
but basically you do a manual call to the api along with any parameters you need, and save the response as a json file.
When you write your test for the api call, send along the same parameters and get it to load in the mock rather than using the live api, you can then test the data in the mock you created contains the expected values.
My Updated version of the api in question can be found here.
Updated Repo
One of the ways to test out external APIs is as you mentioned, by creating a mock and working against that with the behavior hard coded as you have understood it.
Sometimes people refer to this type of testing as "contract based" testing, where you can write tests against the API based on the behavior you have observed and coded against, and when those tests start failing, the "contract is broken". If they are simple REST based tests using dummy data you can also provide them to the external provider to run so they can discover where/when they might be changing the API enough that it should be a new version or produce a warning about not being backwards compatible.
Ref: https://www.thoughtworks.com/radar/techniques/consumer-driven-contract-testing

Webservice test isolation - but when to verify the webservice itself?

I am isolating my webservice-related tests from the actual webservices with Stubs.
How do you/should i incorporate tests to ensure that my crafted responses match the actual webservice ones (i don't have control over it)?
I don't want to know how to do it, but when and where?
Should i create a testsuite-testsuite for testdata testing?...
I would use something like this excellent tool
Storm
If you can, install the service in a small, completely controlled environment. Drawback: You must find a way to be notified when a new version is rolled out.
If that's not possible, write a test that calls the real service and checks for vital points (do I get a response? Are all parts there and where I expect them? Can I parse the result?)
Avoid things like checking timestamps, result size, etc., that is things that can and do change all the time.
You can test the possible failures using EasyMock as follows:
public void testDisplayProductsWhenWebServiceThrowsRemoteLookupException() {
...
EasyMock.expect(mockWebService.getProducts(category)).andThrow(new RemoteLookupException());
...
someServiceOrController.someMethodThatUsesMockWebService(...);
}
Repeat for all possible failure scenarios. The other solution is to implement a dummy SEI yourself. Using JAX-WS, you can trivially annotate a java class that generates an interface consistent with the client you consume. All of the methods can just return dummy data. You can then deploy the services on your own server and point your test environment at the dummy location.
Perhaps more importantly than any of the crap I've said so far, you can take the advice of the authors of The Pragmatic Programmer and program with assertions. That is, given that you must inevitably make certain assumptions about the web service you consume given that you have no control over it's implementation, you can add code such as:
if(resultOfWebService == null || resultOfWebService.getId() == null)
throw new AssertionError("WebService violated contract by doing xyz: result => " + resultOfWebServivce);
That way, if your assumptions don't hold, you'll at least find out about it instead of potentially silently fail!
You can also turn on schema validations and protocol validations to ensure that the service is operating according to spec.