【问题标题】:Mockery & PHPUnit: method does not exist on this mock objectMockery & PHPUnit:这个模拟对象上不存在方法
【发布时间】:2016-09-17 20:29:35
【问题描述】:

你能告诉我问题出在哪里吗?我有一个文件 GeneratorTest.php,其中包含以下测试:

<?php

namespace stats\Test;

use stats\jway\File;
use stats\jway\Generator;

class GeneratorTest extends \PHPUnit_Framework_TestCase
{

    public function tearDown() {
        \Mockery::close();
    }

    public function testGeneratorFire()
    {
        $fileMock = \Mockery::mock('\stats\jway\File');
        $fileMock->shouldReceive('put')->with('foo.txt', 'foo bar')->once();
        $generator = new Generator($fileMock);
        $generator->fire();
    }

    public function testGeneratorDoesNotOverwriteFile()
    {
        $fileMock = \Mockery::mock('\stats\jway\File');
        $fileMock->shouldReceive('exists')
            ->once()
            ->andReturn(true);

        $fileMock->shouldReceive('put')->never();

        $generator = new Generator($fileMock);
        $generator->fire();
    }
}

这里是 FileGenerator 类:

文件.php:

class File
{
    public function put($path, $content)
    {
        return file_put_contents($path, $content);
    }

    public function exists($file_path)
    {
        if (file_exists($file_path)) {
            return true;
        }
        return false;
    }
}

Generator.php:

class Generator
{
    protected $file;

    public function __construct(File $file)
    {
        $this->file = $file;
    }

    protected function getContent()
    {
        // simplified for demo
        return 'foo bar';
    }

    public function fire()
    {
        $content = $this->getContent();
        $file_path = 'foo.txt';

        if (! $this->file->exists($file_path)) {
            $this->file->put($file_path, $content);
        }
    }

}

所以,当我运行这些测试时,我收到以下消息:BadMethodCallException: Method ... ::exists() 在这个模拟对象上不存在

【问题讨论】:

  • 尝试将withAnyArgs()添加到文件的模拟中,例如:$fileMock-&gt;shouldReceive('exists') -&gt;once()-&gt;withAnyArgs() -&gt;andReturn(true);
  • 我试过了,同样的问题依然存在。

标签: php unit-testing mocking phpunit mockery


【解决方案1】:

错误信息对我来说似乎很清楚。您只设置了对 put 方法的期望,而不是 existsexists 方法由所有代码路径中的被测类调用。

public function testGeneratorFire()
{
    $fileMock = \Mockery::mock('\stats\jway\File');
    $fileMock->shouldReceive('put')->with('foo.txt', 'foo bar')->once();

    //Add the line below
    $fileMock->shouldReceive('exists')->once()->andReturn(false);

    $generator = new Generator($fileMock);
    $generator->fire();
}

【讨论】:

  • 一个问题:当我们像这样创建一个 Mock 对象时:$fileMock = \Mockery::mock('\stats\jway\File'); 那么该 Mock 对象中的所有方法都会默认返回 NULL,对吧?如果是,那为什么我们需要像你一样设置exists 方法,它会返回NULL,因此put 方法无论如何都会被执行?
  • 您必须添加shouldIgnoreMissing() 以让模拟对象在您未设置期望时返回 null。在这种情况下,最好是严格并返回 false,因为布尔值是 exists 的接口定义的。如果您将if (! $this-&gt;file-&gt;exists($file_path)) 重写为if ($this-&gt;file-&gt;exists($file_path) === false)),您的单元测试将会中断。
  • "你只是为 put 方法设置了一个期望,但不存在。exists 方法被所有代码路径中的被测类调用。"你能再解释一下吗?为什么我们需要为偶然调用的方法设置测试期望?
  • shouldIgnoreMissing() 确实帮助我避免了一些瓶颈。
猜你喜欢
  • 2015-03-01
  • 2014-08-24
  • 2019-08-13
  • 1970-01-01
  • 2021-06-23
  • 2014-08-16
  • 2013-09-12
  • 2010-12-16
  • 2013-03-24
相关资源
最近更新 更多