Laravel&;Mockery:在不影响数据库的情况下对更新方法进行单元测试


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

好吧,我对单元测试、嘲讽和laravel都很陌生。我正在尝试对我的资源控制器进行单元测试,但我被更新功能卡住了。不确定我是做错了什么还是只是想错了。

这是我的控制器:

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

以下是我的测试:

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

问题是,当我这样做时,我会得到Mockery'Exception'InvalidCountException: Method update() from Mockery_0_Book should be called exactly 1 times but called 0 times.,因为我的控制器中有$book->update($data)。如果我把它改为$this->books->update($data),它会被正确地模拟,数据库不会被触动,但它会在前端使用该函数时更新我的所有记录。

我想我只是想知道如何嘲笑$book-object properly

我够清楚吗?否则请告诉我。谢谢

试着模拟findOrFail方法,不返回new Book,而是返回一个带有更新方法的模拟对象。

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

如果您的数据库是托管依赖项,并且在测试中使用mock,则会导致脆弱的测试。不要嘲笑管理依赖关系。

管理依赖项:您可以完全控制的依赖项。