Laravel 5 - 使用 Mockery 模拟 Eloquent 模型
Posted
技术标签:
【中文标题】Laravel 5 - 使用 Mockery 模拟 Eloquent 模型【英文标题】:Laravel 5 - Using Mockery to mock Eloquent model 【发布时间】:2016-08-17 20:06:41 【问题描述】:一直在寻找整个互联网,但似乎没有找到我的问题的答案。我一直在使用 phpUnit 和 Mockery 在 Laravel 中测试控制器。但是,我似乎没有正确模拟基于 Eloquent 的模型。我确实设法以同样的方式模拟了我的 Auth::user(),尽管这在下面的测试中没有使用。
AddressController 中需要测试的函数:
public function edit($id)
$user = Auth::user();
$company = Company::where('kvk', $user->kvk)->first();
$address = Address::whereId($id)->first();
if(is_null($address))
return abort(404);
return view('pages.address.update')
->with(compact('address'));
ControllerTest 包含 setUp 和 mock 方法
abstract class ControllerTest extends TestCase
/**
* @var \App\Http\Controllers\Controller
*/
protected $_controller;
public function setUp()
parent::setUp();
$this->createApplication();
public function tearDown()
parent::tearDown();
Mockery::close();
protected function mock($class)
$mock = Mockery::mock($class);
$this->app->instance($class, $mock);
return $mock;
AddressControllerTest 扩展 ControllerTest
class AddressControllerTest extends ControllerTest
/**
* @var \App\Models\Address
*/
private $_address;
/**
* @var \App\Http\Controllers\AddressController
*/
protected $_controller;
public function setUp()
parent::setUp();
$this->_controller = new AddressController();
$this->_address = factory(Address::class)->make();
public function testEdit404()
$companyMock = $this->mock(Company::class);
$companyMock
->shouldReceive('where')
->with('kvk', Mockery::any())
->once();
->andReturn(factory(Company::class)->make([
'address_id' => $this->_address->id
]));
$addressMock = $this->mock(Address::class);
$addressMock
->shouldReceive('whereId')
->with($this->_address->id)
->once();
->andReturn(null);
//First try to go to route with non existing address
$this->action('GET', 'AddressController@edit', ['id' => $this->_address->id]);
$this->assertResponseStatus(404);
它不断抛出的错误是:
1) AddressControllerTest::testEdit404
Mockery\Exception\InvalidCountException: Method where("kvk", object(Mockery\Matcher\Any)) from Mockery_1_Genta_Models_Company should be called exactly 1 times but called 0 times.
也许有人有想法?
【问题讨论】:
用 $this->action() 方法替换了 $this->call() 方法,同时检查我的控制器方法是否被调用并且在替换后它确实调用了。但是问题仍然存在。 【参考方案1】:好的,在找到 Jeffrey Way(Laracasts 背后的人)的多个帖子后,建议人们停止嘲笑 Eloquent 对象,而是在内存数据库中使用,我已经尝试过这种方法。我认为这对于将来遇到相同问题的其他用户可能非常有用,所以我将在下面解释。
现在我已经使用 sqlite 编辑了我的 'config/database.php' 文件以支持内存数据库选项:
'sqlite' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
],
接下来在文件顶部您将看到以下配置:
'default' => env('DB_CONNECTION', 'mysql'),
这可以保持不变,这意味着 Laravel 将检查你的 .env 变量以找到一个 'DB_CONNECTION' 或者使用 mysql 作为默认值。这可能是您在像往常一样运行应用程序时想要做的事情。但是,通过测试,您希望覆盖此配置并将数据库配置临时设置为“sqlite”。这可以通过将“DB_CONNECTION”变量添加到您的 .env 文件来完成:
DB_CONNECTION=mysql
最后在你的 phpunit.xml 中,Laravel 用来实例化单元测试的配置文件,你必须告诉在测试时这个变量应该设置为'sqlite':
<env name="DB_CONNECTION" value="sqlite"/>
现在你已经完成了,每次你准备进行测试时,Laravel 都会启动一个不可见的内存数据库。不要忘记将以下行添加到需要数据库的测试中。
use \Illuminate\Foundation\Testing\DatabaseMigrations;
它会告诉 Laravel 在开始测试之前运行你的数据库迁移,所以你可以像往常一样使用这些表。
这种方式非常适合我!希望大家可以使用。
【讨论】:
对我来说很好。谢谢。迁移后我也在为数据库播种。只是一个警告:您会发现@depends testCreateCard
表示法无法传递让我们说从方法到方法的 Id,因为 setUp()
每次测试运行一次,并且每次都会重置数据库。我解决了播种数据并使用硬编码值的问题。以上是关于Laravel 5 - 使用 Mockery 模拟 Eloquent 模型的主要内容,如果未能解决你的问题,请参考以下文章
在 Laravel 4 中使用 Mockery 模拟自定义类
在 Laravel(流明)上使用 Mockery 模拟 Eloquent 模型不起作用