- Do you use extensive mocking in your PHPUnit tests?
- Do you want to keep your tests clean, maintainable and avoid upgrade hell in the future?
- Do you want to have test that actually test somthing?
This set is for you!
composer require rector/mockstan --devNote: Make sure you use phpstan/extension-installer to load necessary service configs.
Disallow usage of any() expectation in mocks to ensure that all mock interactions are explicitly defined and verified.
rules:
- Rector\Mockstan\Rules\AvoidAnyExpectsRule$someMock = $this->createMock(Service::class);
$someMock->expects($this->any())
->method('calculate')
->willReturn(10);❌
$someMock = $this->createMock(Service::class);
$someMock->expects($this->once())
->method('calculate')
->willReturn(10);👍
Require explicit expects() usage when setting up mocks to avoid silent stubs. This is reuired since PHPUnit 12 to avoid silent stubs.
rules:
- Rector\Mockstan\Rules\ExplicitExpectsMockMethodRule$someMock = $this->createMock(Service::class);
$someMock->method('calculate')->willReturn(10);❌
$someMock = $this->createMock(Service::class);
$someMock->expects($this->once())->method('calculate')->willReturn(10);👍
Disallow mocking of forbidden/core classes (e.g. \DateTime, Symfony Request or RequestStack, Iterable, Symfony and Doctine event objects etc.).
rules:
- Rector\Mockstan\Rules\ForbiddenClassToMockRule$dateTimeMock = $this->createMock(\DateTime::class);❌
$dateTime = new \DateTime();👍
Prevent mocking of Doctrine ODM document classes. Use real instances instead.
rules:
- Rector\Mockstan\Rules\NoDocumentMockingRule$docMock = $this->createMock(App\Document\User::class);❌
$user = new App\Document\User();👍
Avoid creating multiple consecutive methods mocks, one for params and other for return. Use single instead.
rules:
- Rector\Mockstan\Rules\NoDoubleConsecutiveTestMockRule$someMock = $this->createMock(SomeClass::class);
$someMock->expects($this->once())
->method('foo')
->willReturnOnConsecutiveCalls(...)
->willReturnCallback(...)❌
$someMock = $this->createMock(SomeClass::class);
$someMock->expects($this->once())
->method('foo')
// handle params in single call
->willReturnCallback(...)👍
Do not mock Doctrine entity classes. Use real object instance instead.
rules:
- Rector\Mockstan\Rules\NoEntityMockingRule$entityMock = $this->createMock(App\Entity\Product::class);❌
$product = new App\Entity\Product();👍
Disallow assigning a mock to a property while another test uses the real object on the same property.
rules:
- Rector\Mockstan\Rules\NoMockObjectAndRealObjectPropertyRule$this->service = $this->createMock(Service::class);
$this->service = new Service();❌
$this->someMock = $this->createMock(AnotherService::class);
$this->realService = new Service();👍
Avoid tests that only create mocks and never assert behavior. Require meaningful assertions with at least once real object to test.
rules:
- Rector\Mockstan\Rules\NoMockOnlyTestRulepublic function testNothing()
{
$someMock = $this->createMock(Dependency::class);
$someMock->expects($this->once())
->method('doSomething')
->willReturn(true);
$this->assertSame($someMock->doSomething());
}❌
public function testSomething()
{
$someMock = $this->createMock(Dependency::class);
$realObject = new RealObject($someMock);
$this->assertTrue($realObject->doSomething());
}👍
Happy coding!