Open
Description
Feature request
Hi !
In order to enforce and clarify the usage of test doubles for everyone, I would like to know if you think it would be beneficial to implement a rule that raises an error when a mock is created without any assertions on being called (essentially making it a stub).
This rule would only be about using the correct terminology for test doubles. It should help to better understand what is going on in our test, and what is our intention.
https://docs.phpunit.de/en/10.5/test-doubles.html
In example :
<?php
// This will raise an error :
public function testSomething(): void
{
$stub = $this->createMock(Foo::class);
$stub->method('foo')->willReturn('bar');
self::assertSame('bar', $stub->foo());
}
// This won't raise an error :
public function testSomething(): void
{
$stub = $this->createStub(Foo::class);
$stub->method('foo')->willReturn('bar');
self::assertSame('bar', $stub->foo());
}
// This won't raise an error :
public function testSomething(): void
{
$mock = $this->createMock(Foo::class);
$mock->expects($this->once())->method('foo')->willReturn('bar');
self::assertSame('bar', $mock->foo());
}
I can try to make a PR for this, but before working on it, I prefer to ask if you think it would be a good improvement.
Metadata
Metadata
Assignees
Labels
No labels
Activity
ChinaskiJr commentedon Jan 31, 2025
Hello @ondrejmirtes ,
We are working on this feature with @brambaud,
We are facing an issue and we would like your advices.
We first want to specify a custom
MethodTypeSpecifyingExtension
, that will assert that aPHPUnit\Framework\MockObject\MockObject&Object
(the return type of a Test casecreateMock
method call) that then makes a call to anexpects
will return aCertainMockType
.For that we're trying to implement this :
But our custom extension is never called, to be accurate, the
isMethodSupported
is never called.We found that in the
TypeSpecifier
class, when the Node expression is\PhpParser\Node\Expr\MethodCall
, then theisMethodSupported
method is only called when there is only one class referenced.As our object is referencing two classes because our instance is an intersection (signature of PHPUnit
createMock
method) :PHPUnit\Framework\MockObject\MockObject&Object
, our extension is never called.Why is there this limitation ? How do you think we should do in our case ?
Thanks by advance for your work and your feedback 🙂
ondrejmirtes commentedon Feb 27, 2025
brambaud commentedon Mar 5, 2025
We'll work on this first then! Thanks for the answer
We think it is important to make the intention behind test doubles clear, as it helps in understanding tests.
Stubs and mocks serve different purposes.
A stub is used to provide predefined responses to method calls without expectation about the object behavior. By using a stub, it is clear that there will be no expectations on it.
Whereas with a mock it is specifically the expectation part which interest us. Using a mock without expectations can be confusing, as it is unclear whether the lack of expectations was intentional or not. This ambiguity may potentially hide bugs/complexities.
Furthermore, it seems to go along with how PHPUnit is expected to be used.
For instance to quote Sebastian Bergmann from this interview
Maybe it could be a rule enabled only if wanted 🤔
If this is considered a rule which is too opinionated for this package, we understand and we'll see to provide it in its own package to complement this one for instance.