Bastian's Blog

Bastian's Blog

Info | HTML | Text | RSS

Wsunit

An awful lot of my daily worktasks are talking to different webservices and/or external APIs over Http. To ensure the quality and functionality of my implementations I switched to test driven development quite some time ago. As you might know one differs tests (or better verifications) in those who verify the smallest runnable unit (unit test) and those who verify the integration of each system under test (SUT). This diversion often forces you to write similar tests for each of the approaches. Which is in most cases copying the unit test to your integration test suite and replace the fixture file by the real request. Getting bored by writing test all over again, I thought of a different and at least for me better approach. So I came up with wsunit.

Testing webservices

The Main purpose of wsunit is to get rid of the boring repetitions or dangerous copy paste testing. Imageine you write your unit tests to verify the behavior of your implementation using the response of a web service in that way that it is using a fixture file. The fixture file itself at least contains the response body and in more advanced cases also the resonpse header. When writing this unit test at least for me the usual behavior is not to rely on a probably provided documentation of the service but sending a request to the service and record it's response. I always make sure I save the header for further processing. The response data (header and body) are stored in a computer readable format (XML) in the fixture file for further processing. Your test now gets the data from the fixture file and verifies whatever implementation you want to be sure of. All good. Now the servide provider decides to alter the response without notifying you. Using a fixture file recorded some time ago you are not able to detect this change by running you test suite. Worse case your credit card payment process gets messed up and the customer will not get charged for his shopping. Somewhen you get somehow notified that there is something wrong with a service. You decide to fix this right away and even write an integration test for it. If you are not as lazy as I am you write a new test sending a real request to the service, receiving it's responce, and using this one for further processing. Then you realize that there are a number of unit tests relying on the correct behavior of the external service. Sure you now start to implement test for each and every possible response of the service to really be sure it behaves as agreed on.

Wsunit to the rescue

This is the point where wsunit plays it's game. Once setup it sends requests to configured locations, fetches the response and saves it for further processing. The benefit for you is that you just have to write unit tests as you already did in the first place. The rest is done by configuration which test shall call which location.

Setup and configuration

First clone wsunit from github or fork it and clone it from your own repository. The configuration has two parts. One is the registration of the actual test listener to PHPUnit, the 2nd is the definition of which test (identified by it's name) shall be request the response of which location.

Since your tests will wait for the service to answer they will run a lot slower as your unit tests. It is a common best practice to separate those integration tests into a separate job on your CI-server to prevent the developer to run those tests while developing.

PHPUnit

For the PHPUnit configuration please visit http://www.phpunit.de/manual/current/en/appendixes.configuration.html and read the 'Test Listener' section or copy the example to your configuration and simply adapt the location of your configuration file.

<listeners> <listener class="WebServiceListener"> <arguments> <object class="Extensions_Webservice_Listener_Factory" /> <object class="Extensions_Webservice_Listener_Loader_Configuration" /> <array> <element key="httpClient"> <string> Extensions_Webservice_Listener_Http_Client </string> </element> <element key="logger"> <string> Extensions_Webservice_Listener_Logger </string> </element> <element key="configuration"> <string> /path/to/configuration.xml </string> </element> </array> </arguments> </listener> </listeners>
Arguments
(object) Extensions_Webservice_Listener_Factory
Factory class providing objects mandatory for the operation of the listener.
(object) Extensions_Webservice_Listener_Loader_Configuration
Object to load the configuration file.
(array) Contains the names of classes to be registered to the factory and
the location of the location definition file.

Test listener configuration

Beside making PHPUnit aware of the test listener and to actually make each test aware of the loaction the response shall be fetched from, a 2nd configuration file is needed. The following example show such a configuration.

The name and the location of the configuration file is set in the element[key='configuration'] element of the test listener registration in PHPUnit.

<?xml version="1.0" encoding="UTF-8"?> <listener> <serializer> Extensions_Webservice_Serializer_Http_Response </serializer> <test case="Example_TestCase" name="testGetData"> <location href="http://example.org/data.txt" /> </test> <test case="Extensions_Webservice_Constraint_JsonErrorMessageProviderTest" name='testTranslateTypeToPrefix with data set "expected"' > <serializer> Extensions_Webservice_Serializer_Http_Response </serializer> <location dataName="expected" href="http://blog.bastian-feder.de/blog.rss" > <query> <param name="mascott[]">tux</param> <param name="mascott[RedHat]">beastie</param> <param name="os">Linux</param> </query> </location> </test> </listener>
Available tags

Beyond setup and configuration

Once you finished the setup and configuration you just call PHPunit as usual and wsunit will handle the fixture files for the configured tests. If a test will be run again wsunit does not override an existing fixture file due to further investigations. But this leaves you with the task to clean up the directory once in a while.

Happy testing!

If you like this article, I'd be very pleased if you'd flattr me.

Trackbacks

Comments

Add new comment

Fields with bold names are mandatory.

Blogroll