如何用 phpunit 模拟 Laravel Eloquent 模型?
Posted
技术标签:
【中文标题】如何用 phpunit 模拟 Laravel Eloquent 模型?【英文标题】:How to mock Laravel Eloquent model with phpunit? 【发布时间】:2021-12-23 07:59:16 【问题描述】:我有一个基于 Illuminate\Database\Eloquent\Model
的 Laravel User
模型。
它在我要进行单元测试的服务中使用。这就是为什么我想模拟具有某些属性的User
,在这种情况下,它是关于应该设置为23
的id
。
我通过以下方式创建我的模拟:
/**
* @param int $id
* @return User|\phpUnit\Framework\MockObject\MockObject
*/
protected function createMockUser(int $id): \PHPUnit\Framework\MockObject\MockObject|User
$user = $this
->getMockBuilder(User::class)
->disableOriginalConstructor()
->getMock();
$user->id = $id;
return $user;
我也试过了:
$user->setAttribute('id', $id);
但在这两种情况下,模拟上的 id
属性将是 null
。
如何在 Laravel 模型的 mock 上设置属性?
【问题讨论】:
我解决了我的问题,方法是不将模型注入我的服务,而是直接注入它的 id,但对于某些服务,这种方法仍然会崩溃(假设我想访问它上面的多个值)。在这种情况下,就足够了。 可以创建一个空的User::class
模型并设置id:new User(['id' => 23]);
或者使用模型工厂:laravel.com/docs/master/…
Mocking Laravel 模型不能解决任何问题,它可以使用工厂创建,而不是使用数据库,该功能可以使用 sqlite 数据库或类似数据库。如果您只需要访问属性,即使没有数据库访问权限的空模型也可以工作。
【参考方案1】:
正如我在评论中所说,您不应该模拟用户。 Laravel
有多种工具可以解决这个问题,主要原因是您必须模拟不必要的长调用链,静态函数和部分模拟不适用于表命名和保存操作。 Laravel
有很好的测试文档,例如。 testing with a database,它回答了Laravel
中的大多数测试最佳实践。
这里有两种方法,您可以创建工厂来创建永远不会保存在数据库中的用户。对工厂调用 create()
会将其保存到数据库中,但调用 make()
会用数据填充模型而不是保存它。
class UserFactory extends Factory
public function definition()
return [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
];
// id is normally set by database, set it to something.
$user = User::factory()->make(['id' => 42]);
取而代之的是单元测试和更多面向功能的测试。为了性能和测试环境中的易用性,默认使用 sqlite
会更容易。
添加以下内容。
config/database.php
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DATABASE_URL'),
'database' => ':memory:',
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
],
phpunit.xml
<server name="DB_CONNECTION" value="sqlite"/>
在你的测试中使用这个特性。
use RefreshDatabase;
现在您应该可以使用以下内容了。这将提供一个完整的用户模型,其中包含您需要的所有功能。 sqlite
的唯一缺点是您使用的版本,不支持外键和奇怪的数据库特定功能。
public function test_your_service()
$result = resolve(YourService::class)->saveUserData(User::factory()->create());
// assert
【讨论】:
感谢您的回答,我必须写一些类似的东西,但您节省了我的时间。我将添加一些指向文档的链接,任何用户在提出任何问题之前都应该阅读这些链接,因为 Laravel 拥有有史以来最好的文档之一......所有内容都在那里进行了解释,而且大部分时间都很清楚...现在,对于作者:文档有自己的testing
部分,所以如果您阅读它来补充您可以在此处找到的任何内容作为答案,那就太棒了...
我不需要像保存这样的数据库操作。基本上,我在符合类名的同时使用模拟作为 pdo。我不需要 SQLite 数据库,因为被测服务仅从模型实例中读取数据。
然后使用第一个解决方案,那里不需要数据库。但是这样关系就行不通了。我见过很多人试图模拟他们的方式模型,这既费时又不实用,所有测试的 laravel 开发人员都做了类似的事情。然后你可以争论你需要什么以及你在其他地方做什么。但这是 laravel 中的标准,一般来说,它对大多数问题都有一种非常务实的方法,并且 taylor 表示他总是想要一个不使用模拟的测试,所以他知道实现是有效的以上是关于如何用 phpunit 模拟 Laravel Eloquent 模型?的主要内容,如果未能解决你的问题,请参考以下文章