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

查看:70
本文介绍了在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 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天全站免登陆