I am writing some unit test for some legacy code as a preparation for some refactoring. One section of the code is calling a class from a library where I cannot change the code in it. The code is using its constructor and calling some static variable. May I ask how can I mock the method for testing? (or how do I test the method without mocking?)
I have tried to simplify my code for an example:
<?php
//This is from the library code that I am unable to change
class ResponseMessage {
const STATUS_ERROR = 500;
const STATUS_SUCCESS = 200;
function __construct( $responseMessage, $responseCode ) {
$this->responseMessage = $responseMessage;
$this->responseCode = $responseCode
}
public function sendMessage($sendToAdmin = false) {
if ($sendToAdmin) {
$targetEmailAddress = $this->emailAddresses['admin'];
$targetAPISettings = $this->apiSettings['admin'];
}
else {
$targetEmailAddress = $this->emailAddresses['customer'];
$targetAPISettings = $this->apiSettings['customer'];
}
$this->tirggerSendEmail($targetEmailAddress);
$this->triggerAPI($targetAPISettings);
}
//.... Some other methods and variables
}
///////////////////////////////////////////////////////////////////
//This is my class to be tested. If possible I would like to avoid changing any code, but if it is necessary this code can be changed a bit for injecting mock class.
class myClass {
// ... some other methods where I have no problem testing....
function handle($input) {
//The actual comparision is much more complicated. Simplified for this question
if (empty($input)) {
// Some other error handling, no response message is generated
return;
}
if ( $input == 1 )
$responseMessage = new ResponseMessage('The input is 1', ResponseMessage::STATUS_SUCCESS );
else if ( $input == 2 )
$responseMessage = new ResponseMessage('The input is 2', ResponseMessage::STATUS_SUCCESS );
else
$responseMessage = new ResponseMessage('Unexpected input', ResponseMessage::STATUS_ERROR );
$respnoseMessage->sendMessage();
$responseMessageToAdmin = new ResponseMessage('The comaparison is triggered', RespnoseMessage::STATUS_SUCCESS);
$responseMessageToAdmin->sendMessage(true);
}
}
///////////////////////////////////////////////////////////////////
//This is my testing class in PHPUnit.
class testMyClass {
public function testProcessWithInputOne {
// how should I mock ResponseMessage here?
/////////////////////////////////////////////////////////
// These are not going to work, but tried to write some code to demo what I want to test.
$mockResponseMessage = Mockery::mock('alias:ResponseMessage');//used alias for simplicity, but mocking it normally and inject the mocked object is also acceptable.
$mockResponseMessage->setConst('STATUS_SUCCESS', 200);
$mockResponseMessage->shouldReceive('__construct')->with('The input is 1', 200)->once()->andReturn($mockResponseMessage);
$mockResponseMessage->shouldReceive('sendMessage')->with(false)->once(); //here with(false) is testing no argument is passed as the default argument value is false
$mockResponseMessage->shouldReceive('__construct')->with('The comaparison is triggered', 200)->once()->andReturn($mockResponseMessage);
$mockResponseMessage->shouldReceive('sendMessage')->with(true)->once();
/////////////////////////////////////////////////////////
$myClass = new MyClass();
$myClass->handle(1);
}
}
2
Answers
If you can modify MyClass, I would do the following. The challenge is that ResponseMessage needs values in the constructor and therefore cannot be mocked the way MyClass is structured.
The ResponseMessageAdapter is more or less a bridge to the ResponseMessage.
You pass the ResponseMessageAdapter to MyClass via the constructor. You can also use it outside the test.
ResponseMessageAdapter::getResponseMessage() returns the instance of ResponseMessage. So you can mock all the methods in ResponseMessage.
}