【问题标题】:Why doesn't this mock the logging call?为什么这不模拟日志记录调用?
【发布时间】:2016-02-02 06:10:43
【问题描述】:

所以我不确定我做错了什么,但是:

class Details {

    public function details($href) {
        $client = new Client();
        $eveLog = new EveLogHandler();

        $response = $client->request('GET', $href);
        $eveLog->requestLog($response, 'eveonline_item_details.log');

        if ($response->getStatusCode() === 200) {
            return json_decode($response->getBody()->getContents());
        }

        return false;
    }
}

我想模拟一下:$eveLog->requestLog($response, 'eveonline_item_details.log');

于是我写了一个测试:

class DetailsTest extends \PHPUnit_Framework_TestCase {

    public function getLogMock() {
        return $this->getMockBuilder('EveOnline\Logging\EveLogHandler')
             ->setMethods(['requestLog'])
             ->getMock();
    }

    public function testDetailsDoesNotReturnFalse() {

        $details = new EveOnline\Items\Details();

        $logger = $this->getLogMock();
        $logger->expects($this->exactly(1))
            ->method('requestLog')
            ->with('request', 'request.log');

        $response = $details->details('http://example.com');

        $this->assertNotFalse($response);
    }
}

接受它不会模拟方法调用,而是会因错误而吓坏:

1) DetailsTest::testDetailsDoesNotReturnFalse
Error: Call to undefined function EveOnline\Logging\storage_path()

现在这个方法:storage_path 是一个 laravel 方法,由于这个库在 laravel 之外,出于开发目的,该方法不存在,因此我试图模拟调用:$eveLog->requestLog($response, 'eveonline_item_details.log');

由此引发了两个问题:

  1. 首先,为什么我的测试没有发现我已经模拟了方法调用这一事实?
  2. 到了测试这个特定方法的时候,你如何模拟全局函数? Laravel 有几个,我需要模拟出 storage_path 方法。

想法?

更新

应该知道,这个类通过提供者类和外观类变成了 laravel 外观,两者都注册到 laravel 允许我做类似的事情

EveDetails::details()

例如。在此处了解更多信息https://laravel.com/docs/5.1/facades

我可以发布外观和提供者类,这会有所帮助。

在我耳中实例化该类的唯一原因是因为这个特定组件正在构建为 laravel 的单独可安装组件。

【问题讨论】:

    标签: php unit-testing mocking laravel-5.2


    【解决方案1】:

    您的方法Details::details() 不知道$eveLog 应该是您的模拟实例而不是类EveLogHandler。您必须使用dependency injection 注入您的记录器实例。

    最简单的方法是把它放到构造函数中:

    class Details {
    
        private $eveLog;
    
        public function __construct($eveLog) {
            $this->eveLog = $eveLog;
        }
    
        public function details($href) {
            $client = new Client();
    
            $response = $client->request('GET', $href);
            $this->eveLog->requestLog($response, 'eveonline_item_details.log');
    
            if ($response->getStatusCode() === 200) {
                return json_decode($response->getBody()->getContents());
            }
    
            return false;
        }
    }
    

    在您的生产代码中,您必须调用:

    $details = new EveOnline\Items\Details(new EveLogHandler());
    

    将真正的 Eventlogger(EveLogHandler 类的实例)注入到 Details 类的实例中。

    您现在将在您的 TestCase 中注入您的模拟:

    $logger = $this->getLogMock();
    $logger->expects($this->exactly(1))
           ->method('requestLog')
           ->with('request', 'request.log');
    
    $details = new EveOnline\Items\Details($logger);
    

    【讨论】:

    • Details 不能有构造函数,因为它从未实例化。是一个 laravel 门面,所以这个类是通过提供者注册的,并变成了一个门面,允许我做类似的事情:'EveDetails::details()'laravel.com/docs/5.1/facades 所以你的方法不起作用。还有什么办法吗?在这里实例化它的唯一原因是因为我正在构建这个与 larvel 分开的组件,以便像库一样使用。
    • 难道你不能在你的 laravel 项目的DI-Container 中添加依赖“Class Details requires instance of class EveLogHandler”以便自动注入吗?我不熟悉 Laravel..
    • 对不起,我没听懂你是说我应该有能力将记录器添加到 DI 容器中吗?如果是,那么不是,为什么?因为这个记录器是我正在构建的库的内部记录器。我不希望您必须设置记录器以及要使用的类,我只希望您设置要使用的类,他们自己使用记录器然后注销响应见上文。
    猜你喜欢
    • 2016-12-07
    • 2011-06-23
    • 1970-01-01
    • 2016-06-04
    • 1970-01-01
    • 2014-02-26
    • 2014-05-24
    • 2016-08-02
    • 2020-08-16
    相关资源
    最近更新 更多