I have a class called \"QueryService\". On this class, there is a function called \"GetErrorCode\". Also on this class is a function called \"DoQuery\". So you can safely sa
To test this class, you would mock the IntegratedService
. Then, the IntegratedService::getResult()
can be set to return what ever you like in a mock.
Then testing becomes easier. You also need to be able to use Dependency Injection to pass the mocked service instead of the real one.
Class:
class QueryService {
private $svc;
// Constructor Injection, pass the IntegratedService object here
public function __construct($Service = NULL)
{
if(! is_null($Service) )
{
if($Service instanceof IntegratedService)
{
$this->SetIntegratedService($Service);
}
}
}
function SetIntegratedService(IntegratedService $Service)
{
$this->svc = $Service
}
function DoQuery($request) {
$svc = $this->svc;
$result = $svc->getResult($request);
if ($result->success == false)
$result->error = $this->GetErrorCode($result->errorCode);
}
function GetErrorCode($errorCode) {
// do stuff
}
}
Test:
class QueryServiceTest extends PHPUnit_Framework_TestCase
{
// Simple test for GetErrorCode to work Properly
public function testGetErrorCode()
{
$TestClass = new QueryService();
$this->assertEquals('One', $TestClass->GetErrorCode(1));
$this->assertEquals('Two', $TestClass->GetErrorCode(2));
}
// Could also use dataProvider to send different returnValues, and then check with Asserts.
public function testDoQuery()
{
// Create a mock for the IntegratedService class,
// only mock the getResult() method.
$MockService = $this->getMock('IntegratedService', array('getResult'));
// Set up the expectation for the getResult() method
$MockService->expects($this->any())
->method('getResult')
->will($this->returnValue(1));
// Create Test Object - Pass our Mock as the service
$TestClass = new QueryService($MockService);
// Or
// $TestClass = new QueryService();
// $TestClass->SetIntegratedServices($MockService);
// Test DoQuery
$QueryString = 'Some String since we did not specify it to the Mock'; // Could be checked with the Mock functions
$this->assertEquals('One', $TestClass->DoQuery($QueryString));
}
}
You need to use PHPUnit
to create your Subject Under Test. If you tell PHPUnit
which methods you would like to mock it mock only these method and the rest of class methods will stay as from original class.
So an example test could look like this:
public function testDoQuery()
{
$queryService = $this->getMock('\QueryService', array('GetErrorCode')); // this will mock only "GetErrorCode" method
$queryService->expects($this->once())
->method('GetErrorCode')
->with($this->equalTo($expectedErrorCode));
}
Anyway, as answer above says, you should also use Dependency Injection
pattern in order to make possible mock IntegratedService
as well (because based on the example above you need to know $result->success
value).
So the right test should look like this:
public function testDoQuery_Error()
{
$integratedService = $this->getMock('\IntegratedService', array('getResult'));
$expectedResult = new \Result;
$expectedResult->success = false;
$integratedService->expects($this->any())
->method('getResult')
->will($this->returnValue($expectedResult));
$queryService = $this->getMockBuilder('\QueryService')
->setMethods(array('GetErrorCode'))
->setConstructorArgs(array($integratedService))
->getMock();
$queryService->expects($this->once())
->method('GetErrorCode')
->with($this->equalTo($expectedErrorCode))
->will($this->returnValue('expected error msg');
$this->assertEquals($expectedResult->error, 'expected error msg');
}
public function testDoQuery_Success()
{
$integratedService = $this->getMock('\IntegratedService', array('getResult'));
$expectedResult = new \Result;
$expectedResult->success = true;
$integratedService->expects($this->any())
->method('getResult')
->will($this->returnValue($expectedResult));
$queryService = $this->getMockBuilder('\QueryService')
->setMethods(array('GetErrorCode'))
->setConstructorArgs(array($integratedService))
->getMock();
$queryService->expects($this->never())
->method('GetErrorCode');
}