简体   繁体   English

Laravel&Mockery:单元测试更新方法而不需要访问数据库

[英]Laravel & Mockery: Unit testing the update method without hitting the database

Alright so I'm pretty new to both unit testing, mockery and laravel. 好吧所以我对单元测试,嘲弄和laravel都很陌生。 I'm trying to unit test my resource controller, but I'm stuck at the update function. 我正在尝试对我的资源控制器进行单元测试,但我仍然坚持更新功能。 Not sure if I'm doing something wrong or just thinking wrong. 不确定我做错了什么或只是想错了。

Here's my controller: 这是我的控制器:

class BooksController extends \BaseController {

    // Change template.
    protected $books;

    public function __construct(Book $books)
    {
        $this->books = $books;
    }

    /**
     * Store a newly created book in storage.
     *
     * @return Response
     */
    public function store()
    {
        $data       = Input::except(array('_token'));
        $validator  = Validator::make($data, Book::$rules);

        if($validator->fails())
        {
            return Redirect::route('books.create')
                ->withErrors($validator->errors())
                ->withInput();
        }

        $this->books->create($data);

        return Redirect::route('books.index');
    }

    /**
     * Update the specified book in storage.
     *
     * @param  int  $id
     * @return Response
     */
    public function update($id)
    {
        $book       = $this->books->findOrFail($id);
        $data       = Input::except(array('_token', '_method'));
        $validator = Validator::make($data, Book::$rules);

        if($validator->fails())
        {
            // Change template.
            return Redirect::route('books.edit', $id)->withErrors($validator->errors())->withInput();
        }

        $book->update($data);

        return Redirect::route('books.show', $id);
    }
}

And here are my tests: 这是我的测试:

public function testStore()
{
    // Add title to Input to pass validation.
    Input::replace(array('title' => 'asd', 'content' => ''));

    // Use the mock object to avoid database hitting.
    $this->mock
        ->shouldReceive('create')
        ->once()
        ->andReturn('truthy');

    // Pass along input to the store function.
    $this->action('POST', 'books.store', null, Input::all());

    $this->assertRedirectedTo('books');
}

public function testUpdate()
{
    Input::replace(array('title' => 'Test', 'content' => 'new content'));

    $this->mock->shouldReceive('findOrFail')->once()->andReturn(new Book());
    $this->mock->shouldReceive('update')->once()->andReturn('truthy');

    $this->action('PUT', 'books.update', 1, Input::all());      

    $this->assertRedirectedTo('books/1');
}

The issue is, when I do it like this, I get Mockery\\Exception\\InvalidCountException: Method update() from Mockery_0_Book should be called exactly 1 times but called 0 times. 问题是,当我这样做时,我得到Mockery\\Exception\\InvalidCountException: Method update() from Mockery_0_Book should be called exactly 1 times but called 0 times. because of the $book->update($data) in my controller. 因为我的控制器中有$book->update($data) If I were to change it to $this->books->update($data) , it would be mocked properly and the database wouldn't be touched, but it would update all my records when using the function from frontend. 如果我要将其更改为$this->books->update($data) ,它将被正确模拟并且不会触及数据库,但是当使用前端函数时它会更新我的所有记录。

I guess I simply just want to know how to mock the $book-object properly . 我想我只是想知道如何$book-object properly模拟$book-object properly

Am I clear enough? 我清楚了吗? Let me know otherwise. 别的我知道。 Thanks! 谢谢!

Try mocking out the findOrFail method not to return a new Book , but to return a mock object instead that has an update method on it. 尝试findOrFail方法不返回new Book ,而是返回一个模拟对象,而不是它有一个更新方法。

$mockBook = Mockery::mock('Book[update]');
$mockBook->shouldReceive('update')->once();
$this->mock->shouldReceive('findOrFail')->once()->andReturn($mockBook);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM