如何为使用静态变量和构造函数从库中调用类的代码编写单元测试?

问题描述 投票:0回答:1

我正在为一些遗留代码编写一些单元测试,作为一些重构的准备。代码的一部分是从库中调用一个类,我无法更改其中的代码。该代码使用其构造函数并调用一些静态变量。请问如何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);
    }
}
php unit-testing phpunit
1个回答
0
投票

如果你可以修改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();
        }
        // ...
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.