php - 使用 mock 对具有依赖关系的类进行单元测试

标签 php unit-testing testing laravel mockery

我是测试新手,我正在尝试创建一个单元测试来覆盖 NewsCreator create 方法中的第一个 if 语句。

这个问题有两个部分。

首先:我应该如何实例化 NewsCreator 来处理模拟的验证器和存储库?

第二:测试这条路径的正确方法是什么?

这是调用需要测试的类的 Controller 方法:

public function store()
{
    $creator = new NewsCreator($this);
    return $creator->create(Input::all());
}

这是我想测试的类,NewsCreator:

<?php namespace WHS\Portal\News;

class NewsCreator {

    protected $listener;
    protected $repository;
    protected $validator;
    protected $errors;

    public function __construct($listener, NewsRepositoryInterface $repository, NewsValidator $validator)
    {
        $this->listener = $listener;
        $this->repository = $repository;
        $this->validator = $validator;
        $this->errors = [];
    }
    public function create($data)
    {
        if($this->validator->fails($data))
        {
            return $this->listener->newsCreationFails($this->validator->messages());
        }
        if($this->repository->create($data))
        {
            return $this->listener->newsCreationSucceeds();
        }
        return $this->listener->newsCreationFails($this->errors);
    }
}

这是我尝试编写的测试,但它失败了 有异常(exception):

2) WHS\Portal\Tests\News\NewsCreatorTest::test_failed_validation Mockery\Exception\InvalidCountException:方法 fails("foo") from Mockery_1_WHS_Portal_News_NewsValidator 应该调用 1 次但调用 0 次。

<?php namespace WHS\Portal\Tests\News;

    use TestCase;
    use Mockery as m;

    class NewsCreatorTest extends TestCase {

        public function tearDown()
        {
            m::close();
        }

        public function test_failed_validation()
        {
            $newsRepo = m::mock('\WHS\Portal\News\DbNewsRepository["create"]');
            $newsValidator = m::mock('\WHS\Portal\News\NewsValidator["fails"]');

            $listener = new NewsListenerStub();
            $listener = m::mock($listener)->makePartial();

            $newsValidator->shouldReceive('fails')->with('foo')->once()->andReturn(true);
            $listener->shouldReceive('newsCreationFails')->once()->with('foo')->andReturn();

            $newsCreator = new \WHS\Portal\News\NewsCreator($listener,$newsRepo,$newsValidator);
            $newsCreator->create([]);
        }
    }

更新测试:

use TestCase;
use Mockery as m;

class NewsCreatorTest extends TestCase {

    public function tearDown()
    {
        m::close();
    }

    public function test_failed_validation()
    {
        $newsRepo = m::mock('\WHS\Portal\News\DbNewsRepository["create"]');
        $newsValidator = m::mock('\WHS\Portal\News\NewsValidator["fails"]');

        $listener = m::mock('\WHS\Portal\Tests\News\NewsListenerStub["newsCreationFails"]');

        $newsValidator->shouldReceive('fails')->with([])->once()->andReturn(true);
        $newsValidator->shouldReceive('messages')->once();
        $listener->shouldReceive('newsCreationFails')->once()->with('foo')->andReturn('foo-bar');

        $newsCreator = new \WHS\Portal\News\NewsCreator($listener,$newsRepo,$newsValidator);

        $result = $newsCreator->create([]);
        $this->assertEquals('foo-bar', $result);
    }
}

stub 类:

class NewsListenerStub
{
    public function newsCreationFails($data)
    {
        return $data;
    }
}

请帮忙。

谢谢。

最佳答案

fails() 方法在您的类中使用$data 参数调用。 在您的单元测试中,您传入一个空数组作为数据 create([])。 您对 validatorMock 的参数期望是期望使用参数 foo 调用 fails()。您必须更改它以匹配空数组。

$newsValidator->shouldReceive('fails')->with([])->once()->andReturn(true);

您还必须在 validatorMock 上指定 validator->messages() 方法,因为它也在您的类中被调用。

$newsValidator->shouldReceive('messages')->once();

要使此测试真正有意义,您必须断言 NewsCreationFails 的结果与 create() 的返回值匹配。

$listener->shouldReceive('newsCreationFails')->once()->with('foo')->andReturn('foo-bar');
...
$result = $newsCreator->create([]);

$this->assertEquals('foo-bar', $result);

关于php - 使用 mock 对具有依赖关系的类进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21807656/

相关文章:

testing - 如何在可移动字段中键入

php - 检查数组 PHP 中是否存在具有特定值的键

php - 如何组合 3 个数组

ruby-on-rails - after_create 回调在测试中不起作用,但在控制台中起作用

testing - 加载测试命名空间

testing - 如何从 clj 运行 Clojure 测试(而不是从 lein 启动)?

php - 无法通过 PEAR 安装 PHPUnit,需要 PEAR Installer >= 1.9.2,无法从 1.9.0 升级 PEAR

php - Zend OPcache 问题(Windows Server 2012 + IIS + Plesk + PHP 7)

javascript - 如何在 Jest 中监视 window.scrollTo?

Python:单元测试以检查对象在不同位置是否相同?