Laravel 5 - 使用Mockery来模仿雄辩的模特儿 [英] Laravel 5 - Using Mockery to mock Eloquent model

查看:406
本文介绍了Laravel 5 - 使用Mockery来模仿雄辩的模特儿的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一直在互联网上查找,但似乎没有找到我的问题的答案。我一直在使用PHPUnit和Mockery潜入Laravel的测试控制器。但是,我似乎没有让我的口述的模型嘲笑正确。我确实设法模仿我的Auth :: user(),虽然这在下面的测试中没有使用。

Been looking all over the internet but don't seem to find an answer to my problem. I've been diving into testing controllers in Laravel using PHPUnit and Mockery. However, I don't seem to get my Eloquent based models mocked correctly. I did manage to mock my Auth::user() the same way, although this is not used in the test below.

需要测试的AddressController中的功能:

Function in AddressController that needs to be tested:

public function edit($id)
{
    $user = Auth::user();
    $company = Company::where('kvk', $user->kvk)->first();
    $address = Address::whereId($id)->first();

    if(is_null($address)) {
        return abort(404);
    }

    return view('pages.address.update')
        ->with(compact('address'));
}

包含setUp和mock方法的ControllerTest

ControllerTest containing setUp and mock method

abstract class ControllerTest extends TestCase
{
    /**
     * @var \App\Http\Controllers\Controller
     */
    protected $_controller;

    public function setUp(){
        parent::setUp();
        $this->createApplication();
    }

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

    protected function mock($class)
    {
        $mock = Mockery::mock($class);
        $this->app->instance($class, $mock);
        return $mock;
    }
}

AddressControllerTest扩展ControllerTest

AddressControllerTest extending ControllerTest

class AddressControllerTest extends ControllerTest
{
    /**
     * @var \App\Models\Address
     */
    private $_address;

    /**
     * @var \App\Http\Controllers\AddressController
     */
    protected $_controller;

    public function setUp(){
        parent::setUp();
        $this->_controller = new AddressController();
        $this->_address = factory(Address::class)->make();
    }

    public function testEdit404(){
        $companyMock = $this->mock(Company::class);
        $companyMock
            ->shouldReceive('where')
            ->with('kvk', Mockery::any())
            ->once();
            ->andReturn(factory(Company::class)->make([
                'address_id' => $this->_address->id
            ]));

        $addressMock = $this->mock(Address::class);
        $addressMock
            ->shouldReceive('whereId')
            ->with($this->_address->id)
            ->once();
            ->andReturn(null);

        //First try to go to route with non existing address
        $this->action('GET', 'AddressController@edit', ['id' => $this->_address->id]);
        $this->assertResponseStatus(404);
    }
}

它一直抛出的错误是:

1) AddressControllerTest::testEdit404
Mockery\Exception\InvalidCountException: Method where("kvk", object(Mockery\Matcher\Any)) from Mockery_1_Genta_Models_Company should be called exactly 1 times but called 0 times.

也许任何人都有想法?

推荐答案

好的,在找到Jeffrey Way(Laracasts背后的人)的多篇文章之后,建议人们停止嘲笑有声的对象,而不是使用内存数据库。我以为这可能对于以后遇到同样问题的其他用户非常有用,所以下面我将解释一下。

Okay, after finding multiple posts by Jeffrey Way (the guy behind Laracasts) recommending people to stop mocking Eloquent objects and instead use in memory databases I've tried that approach. I thought this would perhaps be very usable for other users having the same problems in the future, so I'll explain below.

现在我已经编辑了我的' config / database.php'文件以支持使用sqlite的内存数据库选项:

Right now I've edited the my 'config/database.php' file to support in-memory database option using sqlite:

'sqlite' => [
            'driver'   => 'sqlite',
            'database' => ':memory:',
            'prefix'   => '',
        ],

接下来在文件的顶部,你会看到以下配置:

Next on top of the file you'll see the following configuration:

'default' => env('DB_CONNECTION', 'mysql'),

这可以保持不变,这意味着Laravel将检查您的.env变量以找到DB_CONNECTION,否则使用mysql作为默认值。这可能是你照常运行应用程序时想要做的。但是,通过测试,您将要覆盖此配置并临时将数据库配置设置为sqlite。这可以通过在.env文件中添加DB_CONNECTION变量来实现:

This can stay the same, it means that Laravel will check your .env variables to find a 'DB_CONNECTION' or else use mysql as default. This is probably what you'd like to do when running your application as usual. However with testing you would like to override this configuration and set the database config to 'sqlite' temporarily. This can be done by adding the 'DB_CONNECTION' variable to your .env file:

DB_CONNECTION=mysql 

最后在您的phpunit.xml中,Laravel用于实例化单元测试的配置文件,您必须告诉我们在测试这个变量应设置为'sqlite':

Finally in your phpunit.xml, the configuration file used by Laravel to instantiatie the unit tests, you have to tell that when testing this variable should be set to 'sqlite':

<env name="DB_CONNECTION" value="sqlite"/> 

现在您已经完成,Laravel每次即将开始创建一个无形的内存数据库测试。不要忘记将以下行添加到需要数据库的测试中。

Now you are done and Laravel will start up an invisible in-memory database everytime you are about to go testing. Don't forget to add the following line to tests that need the database.

use \Illuminate\Foundation\Testing\DatabaseMigrations;

它会告诉Laravel在开始测试之前运行数据库迁移,所以你可以使用像你这样的表通常会。

It will tell Laravel run your database migrations before starting the tests, so you can use the tables like you normally would.

这样,它对我来说非常适用!希望你们可以使用它。

This way it works perfectly for me! Hope you guys can use it.

这篇关于Laravel 5 - 使用Mockery来模仿雄辩的模特儿的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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