在 Mockery 中测试链式方法调用 [英] Testing chained method call in Mockery

查看:30
本文介绍了在 Mockery 中测试链式方法调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在控制器中正确模拟对 Eloquent 模型的链接调用.在我的控制器中,我使用依赖注入来访问模型,因此它应该很容易模拟,但是我不确定如何测试链接的调用并使其正常工作.这一切都在 Laravel 4.1 中使用 PHPUnit 和 Mockery.

I'm trying to properly mock a chained call to an Eloquent model in a controller. In my controller I'm using dependancy injection to access the model so that it should be easy to mock, however I'm not sure how to test the chained calls and make it work right. This is all in Laravel 4.1 using PHPUnit and Mockery.

控制器:

<?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'));
    }
}

<小时>

控制器测试:


Controller test:

<?php

class TextbooksControllerText extends TestCase
{
    public function __construct()
    {
        $this->mock = Mockery::mock('Eloquent', 'Textbook');
    }

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

    public function testIndex()
    {
        // Here I want properly mock my chained call to the Textbook
        // model.

        $this->action('GET', 'TextbooksController@index');

        $this->assertResponseOk();
        $this->assertViewHas('textbooks');
    }
}

<小时>

我一直试图通过将这段代码放在测试中的 $this->action() 调用之前来实现这一点.


I've been trying to achieve this by placing this code before the $this->action() call in the test.

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

然而,这会导致错误 Fatal error: Call to a member function with() on an non-object in/app/controllers/TextbooksController.php on line 28.

However, this results in the error Fatal error: Call to a member function with() on a non-object in /app/controllers/TextbooksController.php on line 28.

我也尝试了一个链式替代方案,希望它能奏效.

I've also tried a chained alternative hoping it would do the trick.

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

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

What is the best approach I should take to testing this chained method call with Mockery.

推荐答案

我刚开始测试自己,这整个答案在大多数人看来可能是错误的,但我确实看到人们测试错误的东西很普遍.如果您完全测试方法所做的一切,那么您不是在测试,而只是编写了两次方法.

I'm quite new to testing myself, and this whole answer may be wrong in most people's eyes, but I do see a prevalence of people testing the wrong thing. If you test exactly everything a method does then you're not testing, but just writing a method twice.

你应该把你的代码看作是一个黑匣子——在你编写测试时不要假设知道里面发生了什么.调用具有给定输入的方法,期望输出.有时您需要确保某些其他效果发生了,这就是 shouldReceive 内容的时候.但它再次比此集合链测试更高级 - 您应该测试执行此代码所做的代码的代码已完成,但是正是代码本身发生的.因此,应该以某种方式将集合链提取到某个其他方法,并且您应该简单地测试该方法是否被调用.

You should think of your code as something of a black box - don't presume to know what's going on inside when you write your tests. Call a method with a given input, expect an output. Sometimes you need to ensure that certain other effects have happened, and that's when the shouldReceive stuff comes in. But again it's more high level than this collection chain testing - you should be testing that the code to do what this code does is done, but exactly that the code itself happens. As such, the collection chain should be extracted to some other method somehow, and you should simply test that that method is called.

您测试实际编写的代码(而不是代码的目的)的次数越多,您遇到的问题就越多.例如,如果你需要更新代码以不同的方式做同样的事情(也许 remember(6) 而不是 remember(5) 作为该链的一部分或其他东西)),您还必须更新您的测试以确保 remember(6) 现在被调用,而您根本不应该对其进行测试.

The more you're testing the actual written code (rather than the purpose of the code) the more problems you will have. For example, if you need to update the code to do the same thing a different way (maybe remember(6) not remember(5) as part of that chain or something), you also have to update your test to ensure that remember(6) is now called, when you shouldn't be testing that at all.

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

This advice doesn't just go for chained methods of course, it's for any time you ensure that various objects have various methods called on them when testing a given method.

尽管我不喜欢红色、绿色、重构"这个词,但您应该在这里考虑它,因为您的测试方法有两点失败:

As much as I dislike the term 'red, green, refactor' you should consider it here as there are two points where your testing method is failing:

  • 红色/绿色:当您第一次编写失败的测试时,您的代码不应该包含所有这些 shouldReceive(如果有意义,可能只有一两个,请参见上文) - 如果有,那么您不是在编写测​​试,而是在编写代码.实际上,这表明您先编写代码,然后编写适合代码的测试,这与测试优先 TDD 背道而驰.
  • 重构:假设您首先编写了代码,然后编写了适合代码的测试(或者,嘿,不知何故设法准确地猜测应该在您的测试中编写的代码刚刚神奇地计算出来的内容).这很糟糕,但假设你做到了,因为这不是世界末日.您现在需要重构,但不能不更改测试.您的测试与代码紧密耦合,任何重构都会破坏测试.这又与 TDD 的想法背道而驰.
  • Red/Green: When you first write the failing test, your code shouldn't have all these shouldReceives (maybe one or two if it makes sense to, see above) - if it does, then you're not writing a test but you're writing the code. And really, it's an indication that you wrote the code first then the test to fit the code, which is against test-first TDD.
  • refactor: Assuming you have written the code first, then the test to fit the code (or hey somehow managed to guess exactly what shouldReceives to write in your test that the code just magically worked out). That's bad, but let's say you did it, as it's not the end of the world. You now need to refactor, but you can't without changing your test. You test is so closely coupled to the code, that any refactoring will break the test. That is, again, against the idea of TDD.

即使您不遵循测试优先 TDD,您至少应该意识到重构步骤应该是可行的,而不会破坏您的测试.

Even if you don't follow test-first TDD, you should at least realise that the refactor step should be doable without breaking your tests.

无论如何,那只是我的笨蛋.

Anyway, that's just my tuppence.

这篇关于在 Mockery 中测试链式方法调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆