Page 1 of 1
Beginning testing - how to test NuSOAP app?
Posted: Tue Dec 13, 2005 12:16 pm
by jl
I'm just beginning to see the light as far as unit testing goes and want to setup a test framework for a small application I have.
The application uses NuSOAP quite a lot to get information from a remote server. Accesses to the remote server are to a paid-for service, where it's not really going to be a good idea to send lots of test messages every time I want to test the application.
I have a class that extends NuSOAP, which receives and/or sends data via NuSOAP and communicates with the top level of the app.
How can I handle this situation for testing purposes? (E.g. is there a way to 'inject' a false NuSOAP response in to my class when the app. is in debug mode, without adding actual debug code to the production code...?)
Thanks,
jl
Posted: Tue Dec 13, 2005 12:28 pm
by sweatje
You probably want to look into
Mock Objects.
Posted: Tue Dec 13, 2005 7:37 pm
by Ambush Commander
Also try "Server Stub", which is a more restrictive subset of Mock Objects but has been around longer.
Posted: Tue Dec 13, 2005 8:51 pm
by jl
I understand the concept here (I think), but can't quite interpret the examples as to how you actually use this method.
In simple unit test cases it looks like it's possible to test a class without having to put any test code at all actually inside the class - you test it completely externally. Can't see how you'd do that with mock objects or server stubs (at least with code below...).
Current structure of the class I want to test is this (simplified, obviously):
Code: Select all
require_once('nusoap.php') ;
class BusinessLogic extends SomeOtherFunctionality {
function sendMessage($message) {
$client=new soapclient() ;
$result=$client->send($message) ;
return $result ;
}
function manyFunctionsLikeThis($param1 ... ) {
$message = < build message from $params > ;
$result=$this->sendMessage($message) ;
< do something with result >
}
}
So I'd want a stub or mock object to simulate soapclient... an example of how I'd do this would be greatly appreciated..
jl
Posted: Tue Dec 13, 2005 8:55 pm
by Ambush Commander
Read
The Mock Injection Problem. Though the section leads into a discussion of partial mocks, it is valuable reading for how to implement Mocks.
When one object uses another it is very simple to just pass a mock version in already set up with its expectations. Things are rather tricker if one object creates another and the creator is the one you want to test. This means that the created object should be mocked, but we can hardly tell our class under test to create a mock instead. The tested class doesn't even know it is running inside a test after all.
Posted: Tue Dec 13, 2005 10:21 pm
by jl
Thanks. I was thinking I was going to have to restructure the class somehow, and if I'm reading that correctly it looks like that is the only way to do it. Don't know the correct OO terminology, but making a soapclient creation function instead of declaring it inside sendMessage(), then in testing subclass the class and then over-writing the soapclient creation function with creation of a mock looks like the way to go.. ( ? )
Posted: Tue Dec 13, 2005 10:24 pm
by Ambush Commander
Well, it doesn't have to be that complicated. Marcus Baker offers many solutions, and even though he goes on to show you partial mocks, you don't *have* to use them. For your case, I'd suggest going with the first solution:
Code: Select all
<?php
require_once('socket.php');
class Telnet {
...
function &connect(&$socket, $username, $password) {
$socket->read( ... );
...
}
}
?>
Where you pass the SOAPClient from above. After all, you don't want a new SoapClient to be instantiated every time you send a message, eh?
Posted: Tue Dec 13, 2005 10:48 pm
by jl
so initialise $client outside the class altogether?
Code: Select all
$client=new soapclient() ;
$f=new foo() ;
$f->_client=&$client ;
class foo {
var $_client ;
function sendMessage($message) {
$this->_client->send($message) ;
}
}
If that's correct it looks messy to me, as $client isn't relevant outside foo
Posted: Tue Dec 13, 2005 10:51 pm
by Ambush Commander
If it isn't relevant outside of foo, then you may want to go the optional parameter route. The object can be optionally passed in, or you can use the message object that was instantiated during construction.
I suggest you reread the Mock object and Partial mock documentation and see what seems to work best for you. I seriously don't think that you need to instantiate a new object every sent message.
Posted: Wed Feb 01, 2006 11:17 pm
by JPlush76
I actually created a test suite for a web services app that I was using nusoap with.
Here is a sample of one of the classes I used in my test case. I used a mock object with simpletest so I could verify what was getting sent to nusoap and that the appropriate amount of calls were made.
Code: Select all
function testErrorMessageReturnedFromPostingData()
{
$send_string ='<?xml version="1.0" encoding="iso-8859-1" ?><InventoryUpdateResponse><Success>0</Success><ErrorData><Error>ItemCode GHY inventory not updated, item does not exist.</Error></ErrorData></InventoryUpdateResponse>';
$this->nusoap->expectOnce('send', array($this->baseABSTest->getFakeXMLDataString()));
$this->nusoap->setReturnValue('send', $send_string);
$result = $this->invManager->sendData($this->file);
$expected = false;
$this->assertTrue($result === $expected, "Expected: {$expected} Received: {$result}");
$this->nusoap->tally();
$expected_regex = "/TRANSACTION FAILED(.+)ERROR/";
$result = $this->fileManager->readErrorLogFile();
$this->assertWantedPattern($expected_regex, $result, "Expected Regex Pattern: {$expected_regex} Received: {$result}");
}