silasyudi / inject-mocks
通过#InjectMocks和#Mock注解自动将模拟对象注入测试对象,以加快使用PHPUnit进行单元测试的速度。
v3.0.0
2024-06-20 14:29 UTC
Requires
- php: >=8.1
- silasyudi/optional: ^2.0
Requires (Dev)
- phpunit/phpunit: ^10.0
README
通过#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类中的TYPED属性上;
- 带有#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()
相同。