我正在尝试使用模型工厂在数据提供程序中创建模型。如果我在安装方法或测试中直接使用工厂,它可以工作,但如果我尝试在数据提供程序中使用它,我会收到错误:
1)警告
为MyClassTest :: testSomeMethod指定的数据提供程序无效。
无法找到名称为[默认] [App \ Model \ User]的工厂。
工厂定义:
/** @var \Illuminate\Database\Eloquent\Factory $factory */
$factory->define(\App\Model\User::class, function (Faker\Generator $faker) {
static $password;
return [
'id' => $faker->unique()->numberBetween(1, 10000),
'email_address' => $faker->unique()->safeEmail,
'remember_token' => str_random(10),
'password' => $password ?: $password = bcrypt('secret'),
];
});
$factory->state(\App\Model\User::class, 'guest', function ($faker) {
return [
'role_id' => 9999,
];
});
我打电话给工厂:
factory(\App\Model\User::class)->states('guest')->make();
这是来自Laravel的错误还是我错过了什么?
修改
经过一些调试后,我发现在数据提供程序调用之前没有加载工厂定义,它们在调用setUp()方法时调用(即定义) - 这在数据提供程序调用之后发生 - 因此它不能在数据提供商处找到工厂。
所以在我看来,在数据提供者(或测试类中的任何静态方法)中使用工厂是不可能的。 或者我应该做些什么来在我的数据提供者方法中定义工厂!
答案 0 :(得分:5)
我在另一个问题中找到答案(由同一问题引起)
因此,可以通过根据this answer在数据提供程序方法中调用$this->createApplication();
或$this->refreshApplication();
来解决此问题,
或者根据this answer
代码看起来像这样
public function dataProvider() {
$this->refreshApplication(); // or $this->createApplication();
$user = factory(\App\Model\User::class)->states('guest')->make();
// ...
// another code to generate data ....
// ...
return $someFakeData;
}
我尝试过并且工作过,虽然我觉得它是一种解决方法,而不是事情应该如何运作,但任何“更清洁”的建议都会受到赞赏。
答案 1 :(得分:4)
有可能在数据提供者内部使用工厂,并且仍然使数据库事务正常工作!
今天这让我很沮丧,我想出了一个受this answer启发的解决方案,由于this answer
虽然不漂亮,但是在这里:
更新,我也将其转变为一篇博客文章,其内容更加详细:https://technicallyfletch.com/how-to-use-laravel-factories-inside-a-data-provider/
首先,我修改了使用提供程序的方式。我没有像通常那样期望一个参数列表,而是期望一个可以从中解构参数的函数。这才将工厂的执行推迟到我进入测试用例之后。
/**
* @test
* @dataProvider validationProvider
*/
public function it_validates_payload($getData)
{
// data provider now returns a function which we can invoke to
// destructure our arguments here.
[$ruleName, $payload] = $getData();
$this->post(route('participants.store'), $payload)
->assertSessionHasErrors($ruleName);
}
我的提供者现在变成这样:
public function validationProvider()
{
return [
'it fails if participant_type_id does not exist' => [
function () {
return [
'participant_type_id',
array_merge($this->getValidData(), ['participant_type_id' => null])
];
}
],
'it fails if first_name does not exist' => [
function () {
return [
'first_name',
array_merge($this->getValidData(), ['first_name' => null])
];
}
],
'it fails if last_name does not exist' => [
function () {
return [
'last_name',
array_merge($this->getValidData(), ['last_name' => null])
];
}
],
'it fails if email is not unique' => [
function () {
$existingEmail = factory(Participant::class)->create([
'email' => 'pbeasly@dundermifflin.com'
])->email;
return [
'email',
array_merge($this->getValidData(), ['email' => $existingEmail])
];
}
],
];
}
然后这是重点,但它很好地说明了您可以推迟工厂。 getValidData()
方法仅返回有效数据的数组,因此每个测试一次仅声明一个验证错误。它也使用工厂:
private function getValidData()
{
return [
'practice_id' => factory(Practice::class)->create()->id,
'participant_type_id' => 1,
'first_name' => 'John',
'last_name' => 'Doe',
];
}
有些事情仍然困扰着我
id
后,您可以从状态中提取ID,而不必每次都对数据库进行新的调用。否则,它是当您需要此功能时的可行解决方案,希望当其他人偶然发现此功能时,它会对您有所帮助!
答案 2 :(得分:0)
待办事项
dd(\App\Model\User::class);
要查看它是否返回完全合格的班级名称,如果它没有,则可能是您的问题。