一直在寻找互联网,但似乎没有找到我的问题的答案。我一直在使用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'));
}
包含setUp和mock方法的ControllerTest
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.
也许有人有想法?
答案 0 :(得分:7)
好的,在找到Jeffrey Way(Laracasts背后的人)的多篇帖子后,建议人们停止嘲笑Eloquent对象,而是在内存数据库中使用,我尝试过这种方法。我认为这可能对将来遇到同样问题的其他用户非常有用,所以我将在下面解释。
现在我已经编辑了我的配置/ database.php'文件以使用sqlite支持内存数据库选项:
'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在开始测试之前运行数据库迁移,因此您可以像平常一样使用这些表。
这种方式对我来说非常合适!希望你们能用它。