使用 Mockery Eloquent 模型模拟 Laravel
Posted
技术标签:
【中文标题】使用 Mockery Eloquent 模型模拟 Laravel【英文标题】:Laravel mock with Mockery Eloquent models 【发布时间】:2014-11-05 02:42:57 【问题描述】:我正在开发一个带有 laravel(4.2) 框架的 PHP (5.4.25) 应用程序。我想用 Mockery 测试我的 UserController,所以我用这种方式安装了我的 UserController:
class UsersController extends \BaseController
protected $user;
public function __construct(User $user)
$this->user = $user;
$this->beforeFilter('csrf', array('on'=>'post'));
public function store()
$validator = Validator::make(Input::all(), User::$rules);
if ( $validator->passes() )
$this->user->username = Input::get('username');
$this->user->password = Hash::make(Input::get('password'));
$this->user->first_name = Input::get('first_name');
$this->user->last_name = Input::get('last_name');
$this->user->email = Input::get('email');
$this->user->save();
return true;
else
return false;
我想要模拟 Eloquent User 模型,所以我开发了我的 UsersControllerTest:
class UsersControllerTest extends TestCase
private $mock;
public function __construct()
public function setUp()
parent::setUp();
$this->createApplication();
public function tearDown()
parent::tearDown();
Mockery::close();
public function testStore()
$this->mock = Mockery::mock('Eloquent','User[save]');
$this->mock
->shouldReceive('save')
->once()
->andReturn('true');
$this->app->instance('User', $this->mock);
$data['username'] = 'qwerety';
$data['first_name'] = 'asd';
$data['last_name'] = 'asd123';
$data['email'] = 'test@gmail.com';
$data['password'] = 'password';
$data['password_confirmation'] = 'password';
$response = $this->call('POST', 'users', $data);
var_dump($response->getContent());
当我运行我的测试时,它会返回这个错误:
Mockery\Exception\InvalidCountException : Method save() from Mockery_0_User should be called
exactly 1 times but called 0 times.
为什么?怎么了?
编辑: 我发现了问题 - 如果我不使用模拟对象一切正常,并且控制器在数据库中创建一个新用户,但是当我使用模拟时,Input:all()
方法返回空数组。
-- 谢谢
【问题讨论】:
【参考方案1】:您测试这个的方式是,在控制器构造函数中传递一个真实 Eloquent 模型的实例,而不是模拟。如果你想测试门面(正如documentation 中明确说明的那样),你应该直接在门面上调用shouldReceive()
方法来模拟它。
但是,由于您在控制器的store()
方法中重新定义了$this->user
变量,因此不会调用它,除非您删除硬编码的实例化并使用注入的$user
。
编辑:我忽略了$this->app->instance('User', $this->mock);
行。因此,问题可能是由于在 store
方法中您直接获得了一个新的类实例,而不是通过 Laravel IoC 容器。而不是你的 store 方法中的$this->user = new User;
,你应该通过App::make('User');
得到它
【讨论】:
在同样的错误中也删除了 store 方法中的新类实例。【参考方案2】:当我开始测试时,我遇到了同样的问题..... 问题是,在您的 userscontroller 存储方法中,您实际上是将用户保存到数据库并基于您的代码它可能工作得很好,但您会惊讶于它实际上没有通过测试。 现在这样想,你模拟用户,你告诉你的测试,当我调用用户模型时,请给我我的用户的模拟版本,就像你在下面的代码行中所做的那样
$this->app->instance('User', $this->mock);
现在的问题是,您还需要从 User 的模拟版本调用 save() 方法的模拟版本,如下所示:
$this->mock->save();
考虑以下示例:
public function testStore()
$input = ['name', 'Canaan'];
$this->mock
->shouldReceive('create')
->once()->with($input);
$this->app->instance('User', $this->mock);
$this->mock->create($input);
$this->call('POST', 'users', $input);
希望对你有帮助。
【讨论】:
如果我不使用模拟对象,一切正常,控制器在数据库中创建一个新用户,但是当我使用模拟时,Input:all()
方法返回空数组。
在测试类上再次调用$this->mock->create($input)
是否会破坏逻辑。对我来说,我觉得这是在欺骗测试并使其通过控制器测试,这意味着无论控制器(被测试的控制器)是否调用$this->user->save();
,测试都将通过【参考方案3】:
问题出在$this->createApplication();
。
我已经评论了那行,Input::all()
可以很好地处理所有输入参数!
【讨论】:
以上是关于使用 Mockery Eloquent 模型模拟 Laravel的主要内容,如果未能解决你的问题,请参考以下文章
使用 Mockery Eloquent 模型模拟 Laravel
在 Laravel(流明)上使用 Mockery 模拟 Eloquent 模型不起作用