Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 模拟测试链式方法调用_Php_Testing_Laravel_Phpunit_Mockery - Fatal编程技术网

Php 模拟测试链式方法调用

Php 模拟测试链式方法调用,php,testing,laravel,phpunit,mockery,Php,Testing,Laravel,Phpunit,Mockery,我试图正确地模拟控制器中对雄辩模型的链式调用。在我的控制器中,我使用依赖性注入来访问模型,这样它应该很容易模拟,但是我不确定如何测试链接调用并使其正常工作。这一切都在使用PHPUnit和mocky的Laravel4.1中完成 控制器: <?php class TextbooksController extends BaseController { protected $textbook; public function __construct(Textbook $tex

我试图正确地模拟控制器中对雄辩模型的链式调用。在我的控制器中,我使用依赖性注入来访问模型,这样它应该很容易模拟,但是我不确定如何测试链接调用并使其正常工作。这一切都在使用PHPUnit和mocky的Laravel4.1中完成

控制器:

<?php

class TextbooksController extends BaseController
{
    protected $textbook;

    public function __construct(Textbook $textbook)
    {
        $this->textbook = $textbook;
    }

    public function index()
    {
        $textbooks = $this->textbook->remember(5)
            ->with('user')
            ->notSold()
            ->take(25)
            ->orderBy('created_at', 'desc')
            ->get();

        return View::make('textbooks.index', compact('textbooks'));
    }
}
但是,这会导致错误
致命错误:在第28行的/app/controllers/TextbooksController.php中的非对象上使用()调用成员函数

我还尝试了一种链式替代方案,希望它能奏效

$this->mock->shouldReceive('remember')->with(5)->once()
    ->shouldReceive('with')->with('user')->once()
    ->shouldReceive('notSold')->once();
$this->app->instance('Textbook', $this->mock);

用mocky测试这个链式方法调用的最佳方法是什么。

我发现了这种技术,但我不喜欢它。非常冗长。我认为必须有一个更干净/更简单的方法来实现这一点

在构造函数中:

$this->collection = Mockery::mock('Illuminate\Database\Eloquent\Collection')->shouldDeferMissing();
在测试中:

$this->mock->shouldReceive('remember')->with(5)->andReturn($this->mock);
$this->mock->shouldReceive('with')->with('user')->andReturn($this->mock);
$this->mock->shouldReceive('notSold')->andReturn($this->mock);
$this->mock->shouldReceive('take')->with(25)->andReturn($this->mock);
$this->mock->shouldReceive('orderBy')->with('created_at', 'DESC')->andReturn($this->mock);
$this->mock->shouldReceive('get')->andReturn($this->collection);

我对测试自己还很陌生,在大多数人看来,这个答案可能是错误的,但我确实看到很多人在测试错误的东西。如果您完全测试一个方法所做的一切,那么您不是在测试,而是在编写一个方法两次

你应该把你的代码看作是一个黑匣子——当你编写测试时,不要想当然地知道里面发生了什么。使用给定的输入调用一个方法,期望得到一个输出。有时,你需要确保某些其他的效果已经发生,而这时应该接收的东西进来了。但是,它比这个集合链测试更高层次——您应该测试代码是否完成了该代码所做的事情,但代码本身确实发生了。因此,应该以某种方式将收集链提取到其他方法,并且您应该简单地测试该方法是否被调用

测试实际编写的代码越多(而不是代码的目的),问题就越多。例如,如果您需要更新代码以以以不同的方式执行相同的操作(可能是
记住(6)
记住(5)
作为该链的一部分或其他内容),那么您还必须更新测试以确保现在调用
记住(6)
,而您根本不应该进行测试

当然,这个建议不仅仅适用于链式方法,它适用于任何时候,您都可以确保在测试给定的方法时,各种对象都可以调用各种方法

尽管我不喜欢“红、绿、重构”这个词,但在这里你应该考虑一下,因为你的测试方法有两个缺点:

  • 红色/绿色:当您第一次编写失败的测试时,您的代码不应该有所有这些
    shouldReceive
    s(如果有意义,可以有一个或两个,请参见上文)-如果有,那么您不是在编写测试,而是在编写代码。实际上,这表明您先编写代码,然后编写测试以适应代码,这与测试优先TDD相反
  • 重构:假设您首先编写了代码,然后进行测试以适应代码(或者他们以某种方式精确地猜测代码刚刚神奇地解决了您的测试中应该编写什么)。这很糟糕,但假设你做到了,因为这不是世界末日。您现在需要重构,但如果不更改测试,就无法重构。您的测试与代码紧密耦合,任何重构都会破坏测试。这同样与TDD的想法背道而驰
即使您没有遵循TestFirstTDD,您至少应该意识到重构步骤应该是可行的,而不会破坏您的测试


不管怎么说,这只是我的便士。

最初是一条评论,但为了让代码清晰易读,改为回答

我也倾向于,尽管中间立场是:

$this->mock->shouldReceive('remember->with->notSold->take->orderBy->get')
    ->andRe‌​turn($this->collection);

请阅读文档,我也知道这并没有像所问的那样直接回答问题,但我认为这是一个很好的答案,因为它回答了您代码中更广泛的问题,并且对整个社区都有帮助。是的,这是一个很好的答案。实际上,通读它让我觉得有点愚蠢,因为它现在看起来更明显了;测试最终结果是否符合预期,并且仅测试对该过程至关重要的更高级别的代码。我将在下面留下我的答案,因为它确实在技术上实现了我最初寻找的结果,但这是一种错误的方法。我们一起回答了问题的两个方面:)与其直接使用控制器中的模型,不如使用存储库类,并检查控制器是否按其应该的方式调用它:然后,repository类应该使用集成测试(而不是单元测试,因为@alexrussell在他的回答中解释了这些原因)。这是可行的,但我注意到它会导致代码覆盖率失败(如果您正在使用),我不知道,所以感谢您指出这一点。另一个不被吸入测试单元内部的原因是:)@petercoles 3年后,但$this->mock不可用。如何实例化?OP在其TextBookControllerTest(名称稍有错误)中的构造函数中设置$this->mock,这可能是为了避免在设置方法中重复执行,尽管我个人还是更喜欢后者。
$this->mock->shouldReceive('remember')->with(5)->andReturn($this->mock);
$this->mock->shouldReceive('with')->with('user')->andReturn($this->mock);
$this->mock->shouldReceive('notSold')->andReturn($this->mock);
$this->mock->shouldReceive('take')->with(25)->andReturn($this->mock);
$this->mock->shouldReceive('orderBy')->with('created_at', 'DESC')->andReturn($this->mock);
$this->mock->shouldReceive('get')->andReturn($this->collection);
$this->mock->shouldReceive('remember->with->notSold->take->orderBy->get')
    ->andRe‌​turn($this->collection);