【发布时间】:2021-11-06 19:25:09
【问题描述】:
我正在尝试测试我的代码的特定部分,但是 Model 类的某些依赖项使该测试非常难以执行。如果这些依赖关系在注入的类上,我可以轻松地模拟该类。当它来自工厂的数据库关系时,是否可以做类似的事情?我正在将这个想法转换为 Laravel 文档中的示例,以使其更容易举例说明:
use App\Models\Post;
use App\Models\User;
$user = User::factory()
->has(Post::factory()->count(3))
->create();
假设我正在测试一个控制器,它将为用户的帖子创建 cmets。我将使用工厂来创建用户并发布并执行对路由 /api/cmets 的测试发布调用,该帖子具有字段 post_id (int) 和 comment (text)。 但是,Comments 控制器将调用 Post::canAddComment() 方法,该方法将执行大量验证以验证是否可以创建评论。所有这些验证都超出了我的测试范围。
是否可以使用 Post 模型类的 Mock,所以我可以制作,例如:
$postMock->shouldReceive('canAddComment')->once()->andReturn(true);
所以我不需要创建一个完整的场景来进行测试?
可能的解决方案:
扩展 Post 类,仅用于测试:
class PostThatAlwaysAllowsComments extends Post
{
public function canAddComment() {
return true;
}
}
然后在测试中:
use App\Models\Post;
use App\Models\User;
$user = User::factory()
->has(PostThatAlwaysAllowsComments::factory()->count(3))
->create();
【问题讨论】:
-
你能包含 canAddComment() 吗,它看起来像是静态的,没有模拟静态函数的好方法。通常我不会关心 canAddComment 的副作用并断言你正在测试什么,什么是破坏?如果是外部 api 等,为什么不只是模拟一下
-
它不是静态的。我测试的内容取决于这个函数的结果。当然,这与帖子和评论无关,但它是一个验证功能,不使用外部 API,但需要另外 5 个工厂来创建正确的场景。我最终创建了这个场景并且测试正在运行,但是测试变得太复杂了,并且验证功能本身已经在它自己的测试中进行了测试......如果没有其他更好的选择,也许我应该尝试将这段代码分解成更小的可测试的部分..
-
有一些选项,但我需要一些示例代码来提出如何做的建议:P Post::canAddComment() 是一个静态调用 btw
-
我的错,我没有写正确..我只是想写 canAddComment() 是 Post 类中的一个方法:D
-
我不允许在这里放真正的代码,对不起……但是如果这有帮助,一切都在同一个类中完成(示例中的 Post),一切都是 SQL 和业务逻辑。我刚刚想到的一个想法是创建一个扩展模型 Post 的类,更改我需要的内容,并为它创建一个工厂。这种方法的缺点是最终会在要测试的代码库中使用测试类......
标签: php laravel unit-testing mocking mockery