SOAP::Lite and Test::MockModule
Intro
As part of a project to convert an existing SOAP client library over to a new version of calls, I found that I needed a way to test potential faults and new data formats without requiring live calls. After I built it for the new stuff, I used it to test existing calls and errors as well. This code was running live for several years before I needed to change it for the new version, this inspired me to be very careful and invest in testing. On a side note, I found plenty of bugs just creating the test suite in the existing code that had been there for years.Creating the Test Suite
To create the test suite, I needed to pretend to get SOAP server responses. I turned to Test::MockModule to step in and provide hooks to return them. I found that mocking these responses were pretty straightforward once I figured out how to use the SOAP::Lite deserializer. (like most things, its easy once you know how)The following outlines what I did to create the suite, well at least the process of using MockModule to mock the responses.
Overall, there are three high level steps:
- Capturing or constructing the SOAP XML responses
- Locating SOAP::Lite calls in our client library that dispatch requests
- Constructing needed Mocked SOAP::Lite methods
Our code made requests by passing a XML document (that was the request) to the SOAP::Lite call method. Its a bit of an unusually approach to use with SOAP::Lite. I think a little exploration into using MockModule with the autodispatched method might be interesting, another day. This post will show how to mock responses returned from the call method.
Mocking it
Test::MockModule requires you to first declare the module that you want to mock. In my case, we used SOAP::Lite as composite in a class instead of sub class.First, I had MockModule pretend to be a SOAP::Lite object.
my $lite = Test::MockModule->new('SOAP::Lite');Next I created my object that contained a SOAP::Lite object:
$soap = Our::Client::SOAP->new( %params );Then I setup the mocking of the call method. Since my later test calls need to use the data returned from the method, I need to mock the a response with real data returned. In the snippet below, get_good_response returns a XML document. Using the SOAP::Lite deserializer ensures that I test the parsing of the response by SOAP::Lite (instead of returning the expected data structure).
## fake out call with error responseNext I run our client's method that uses the call. In this case the interface is not really the best but try to look beyond that.
$lite->mock(
call => sub { return SOAP::Deserializer->deserialize( get_good_response() ); }
);
( $ret, $res ) = $soap->process;Lastly I check the data $ret and $res for data. In my case $ret returns -1 on error (please in your own libraries throw faults instead of strange return codes).
is $ret, -1, "process ret";That is it. Of course I had many mock calls to simulate the server not responding and various faults.
is $res->dataof('//BirthDay'), '2009-01-15', 'BirthDay Result';
Here a complete example:
use Test::More qw( no_plan );The End... for now
use Test::MockModule;
use Test::Exception;
use Carp; ## for faking out soap::lite
use File::Slurp qw( slurp );
BEGIN { use_ok 'Our::Client::SOAP'; }
{
my $soap;
my ($res, $ret);
my $lite = Test::MockModule->new('SOAP::Lite');
lives_ok { $soap = Our::Client::SOAP->new( %params ); } 'Constructor';
$lite->mock(
call => sub { return SOAP::Deserializer->deserialize(get_fault_resp_ver1()); }
);
lives_ok { ( $ret, $res ) = $soap->process } 'process - fault';
cmp_ok $ret, '==', -1, "process ret";
ok $res->fault, "fault exists";
}
sub get_fault_resp_ver1 { return scalar slurp q{fault_version_1.xml} }
Thank you for SOAP::Lite mocking manual
ReplyDelete