我正在为一些遗留代码编写一些单元测试,作为一些重构的准备。代码的一部分是从库中调用一个类,我无法更改其中的代码。该代码使用其构造函数并调用一些静态变量。请问如何mock测试方法呢? (或者如何在不嘲笑的情况下测试该方法?)
我尝试简化我的代码作为示例:
<?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);
}
}
如果你可以修改MyClass,我会执行以下操作。挑战在于 ResponseMessage 需要构造函数中的值,因此不能以 MyClass 的结构方式进行模拟。
ResponseMessageAdapter 或多或少是 ResponseMessage 的桥梁。
您通过构造函数将 ResponseMessageAdapter 传递给 MyClass。您也可以在测试之外使用它。
ResponseMessageAdapter::getResponseMessage() 返回 ResponseMessage 的实例。这样你就可以模拟ResponseMessage中的所有方法了。
class ResponseMessageAdapter {
private string $responseMessage;
private int $responseCode;
public function setResponseMessage(string $responseMessage): void
{
$this->responseMessage = $responseMessage;
}
public function setResponseCode(int $responseCode): void
{
$this->responseCode = $responseCode;
}
public function getResponseMessage(): ResponseMessage
{
return new ResponseMessage(
$this->responseMessage,
$this->responseCode
);
}
}
class MyClass {
private ResponseMessageAdapter $responseMessageAdapter;
public function __construct(ResponseMessageAdapter $responseMessageProxy)
{
$this->responseMessageAdapter = $responseMessageProxy;
}
public function handle($input)
{
if ($input == 1) {
$this->responseMessageAdapter->setResponseMessage('The input is 1');
$this->responseMessageAdapter->setResponseCode(ResponseMessage::STATUS_SUCCESS);
$responseMessage = $this->responseMessageAdapter->getResponseMessage();
$responseMessage->sendMessage();
}
// ...
}
}