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

时间:2014-09-17 14:15:47

标签: php unit-testing laravel laravel-4 mockery

好吧所以我对单元测试,嘲弄和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

我清楚了吗?别的我知道。谢谢!

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:0)

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

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