symfonyboot / inject-mocks
通过 #InjectMocks 和 #Mock 注解自动将模拟对象注入测试主题,以加快 PHPUnit 的单元测试。
Requires
- php: >=8.1
- silasyudi/optional: ^2.0
Requires (Dev)
- phpunit/phpunit: ^10.0
README
Inject-Mocks
通过 #InjectMocks 和 #Mock 注解自动将模拟对象注入测试主题,以加快 PHPUnit 的单元测试。
摘要
语言 / Idioma
阅读葡萄牙语版本 🇧🇷 这里。
安装
在开发环境中安装
composer require --dev silasyudi/inject-mocks
需求
- PHP 8.3+
- Composer 2
特性
在测试类中使用 #InjectMocks 和 #Mock 注解,可以自动将模拟对象注入测试主题。
在典型场景中,我们可能会这样做
没有 #InjectMocks/#Mock 的示例
class SomeTest extends \PHPUnit\Framework\TestCase { public void testSomething() { $someDependency = $this->createMock(Dependency::class); $anotherDependency = $this->createMock(AnotherDependency::class); ... $subject = new Service($someDependency, $anotherDependency, ...); ... } ...
这种方法会带来维护困难,因为如果测试主题发生变化,无论是添加、减少还是替换依赖项,都必须在每次测试中更改它。
有了 #InjectMocks/#Mock 注解,我们抽象化了这些测试主题的变化。示例
带有 #InjectMocks/#Mock 的示例
use SilasYudi\InjectMocks\InjectMocks; use SilasYudi\InjectMocks\Mock; use SilasYudi\InjectMocks\MockInjector; class SomeTest extends \PHPUnit\Framework\TestCase { #[Mock] private Dependency $someDependency; #[Mock] private AnotherDependency $anotherDependency; ... #[InjectMocks] public function setUp() : void { MockInjector::inject($this); } public void testSomething() { // $this->subject e as dependências já estão instanciadas. } ...
用法
如前一个主题中的示例所示,#InjectMocks 属性必须放置在想要测试的测试主题属性上,而 #Mock 属性必须放置在想要模拟或注入的对应依赖项的属性上。
然后,使用句子 MockInjector::inject($this) 运行注入服务。这个执行可以在每个测试中或 setUp 中声明。
执行注入后,标记为 #InjectMocks 的 service 将是一个在测试类作用域中可用的真实实例,而标记为 #Mock 的每个依赖项都将是一个 MockObject 实例,通过构造函数注入到测试主题中,并且也将可在测试类作用域中使用。
详细信息
1. 属性范围
- #InjectMocks 和 #Mock 必须放置在 TestCase 类中类型的属性上;
- 接收 #InjectMocks 和 #Mock 属性的属性必须是对象;
- 每个 TestCase 只能使用一个 #InjectMocks 属性。当在相同的作用域中使用多个时,此库将仅使用第一个,并忽略其他属性;
- 您必须为每个要模拟的测试主题依赖项使用一个 #Mock 属性;
- 在未类型化的属性或原始数据类型上使用属性将导致
MockInjectException异常。 - 当在同一测试类中使用相同类型的多个对象时,此库将通过属性名称进行匹配,这些名称必须与测试主题类相同。
2. 行为
#InjectMocks 和 #Mock 可以独立使用,也可以一起使用。每个的详细信息
2.1. #InjectMocks
它将通过构造函数创建一个真实实例,如果构造函数中有参数,将按以下顺序使用以下值
- 从 #[Mock] 属性创建的
mock,如果存在的话; - 如果是可选参数,则使用
default值; - 如果它被标记为
null,则使用null; - 如果它不是原始数据类型,则创建一个
mock。在这种情况下,此mock不会注入到 TestCase 作用域中; - 如果前面的选项不满足,则将抛出
MockInjectException异常。
注意:您可以在测试主题的所有、一些或没有依赖项上使用 #Mock 属性。
2.2. #Mock
将在TestCase作用域中创建一个mock,而不使用构造函数。这种创建行为与TestCase::createMock()相同。