dongzhang1987 2014-09-17 14:15
浏览 124
已采纳

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

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.

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.

I guess I simply just want to know how to mock the $book-object properly.

Am I clear enough? Let me know otherwise. Thanks!

  • 写回答

1条回答 默认 最新

  • douwen9345 2014-09-17 14:58
    关注

    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.

    $mockBook = Mockery::mock('Book[update]');
    $mockBook->shouldReceive('update')->once();
    $this->mock->shouldReceive('findOrFail')->once()->andReturn($mockBook);
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 帮我写一个c++工程
  • ¥30 Eclipse官网打不开,官网首页进不去,显示无法访问此页面,求解决方法
  • ¥15 关于smbclient 库的使用
  • ¥15 微信小程序协议怎么写
  • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
  • ¥20 怎么用dlib库的算法识别小麦病虫害
  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教