Laravel & Mockery:单元测试更新方法而不访问数据库
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25893057/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Laravel & Mockery: Unit testing the update method without hitting the database
提问by Mattias
Alright so I'm pretty new to both unit testing, mockery and 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.
好的,所以我对单元测试、嘲笑和 Laravel 都很陌生。我正在尝试对我的资源控制器进行单元测试,但我被困在更新功能上。不知道是我做错了什么还是只是想错了。
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.
because of the $book->update($data)
in my controller. 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.
问题是,当我这样做时,我得到了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)
,它将被正确模拟并且不会触及数据库,但是在前端使用该函数时它会更新我的所有记录。
I guess I simply just want to know how to mock the $book-object properly
.
我想我只是想知道如何模拟$book-object properly
.
Am I clear enough? Let me know otherwise. Thanks!
我够清楚了吗?否则让我知道。谢谢!
回答by Jeff Lambert
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
方法不返回 a new Book
,而是返回一个模拟对象,而不是在其上具有更新方法。
$mockBook = Mockery::mock('Book[update]');
$mockBook->shouldReceive('update')->once();
$this->mock->shouldReceive('findOrFail')->once()->andReturn($mockBook);