Closed davidbyoung closed 5 years ago
I believe this PR should fix the problem
I can still reproduce the warning in the most recent version of PHPUnit (3e9a1656a) using the example above.
Name | Version |
---|---|
PHPUnit version | 3e9a1656a |
PHP version | 7.2.19 + 7.3.6 |
Installation Method | composer |
composer info | sort
doctrine/instantiator 1.2.0
myclabs/deep-copy 1.9.1
phar-io/manifest 1.0.3
phar-io/version 2.0.1
phpdocumentor/reflection-common 1.0.1
phpdocumentor/reflection-docblock 4.3.1
phpdocumentor/type-resolver 0.4.0
phpspec/prophecy 1.8.1
phpunit/php-code-coverage 7.0.5
phpunit/php-file-iterator 2.0.2
phpunit/php-text-template 1.2.1
phpunit/php-timer 2.1.2
phpunit/php-token-stream 3.0.1
sebastian/code-unit-reverse-lookup 1.0.1
sebastian/comparator 3.0.2
sebastian/diff 3.0.2
sebastian/environment 4.2.2
sebastian/exporter 3.1.0
sebastian/global-state 3.0.0
sebastian/object-enumerator 3.0.3
sebastian/object-reflector 1.1.1
sebastian/recursion-context 3.0.0
sebastian/resource-operations 2.0.1
sebastian/type 1.1.1
sebastian/version 2.0.1
symfony/polyfill-ctype v1.11.0
theseer/tokenizer 1.1.3
webmozart/assert 1.4.0
I was able to reproduce the issue with
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
interface I
{
public function m();
}
final class Test extends TestCase
{
public function testOne(): void
{
$i = $this->createMock(I::class);
$i->method('m')->willReturn(new stdClass);
$this->assertInstanceOf(stdClass::class, $i->m());
}
}
https://github.com/sebastianbergmann/type/commit/26a5e767f2970d5651c40625a6e2407c18e19916 (part of version 1.1.2 of the type package) fixes https://github.com/sebastianbergmann/phpunit/issues/3706#issuecomment-503408399 for me.
@sebastianbergmann it's still happening to me when a mock of an interface returns another mock...
interface FeatureRepositoryInterface
{
public function findOneByNameAndContext(string $name, Context $context): ?FeatureInterface;
}
$featureMock = $this->createMock(FeatureInterface::class)
->expects($this->once())
->method('isActive')
->willReturn($expectedIsActive);
$this->createMock(FeatureRepositoryInterface::class)
->expects($this->once())
->method('findOneByNameAndContext')
->with($featureName, $context)
->willReturn($featureMock);
Method findOneByNameAndContext may not return value of type object
Version of phpunit: 8.2.3 Version of type: 1.1.3
@davidkmenta Your example was incomplete. I have completed it like so:
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
interface FeatureInterface
{
public function isActive(): bool;
}
interface FeatureRepositoryInterface
{
public function findOneByNameAndContext(string $name, Context $context): ?FeatureInterface;
}
final class Test extends TestCase
{
public function testOne(): void
{
$featureMock = $this->createMock(FeatureInterface::class)
->expects($this->once())
->method('isActive')
->willReturn(true);
$x = $this->createMock(FeatureRepositoryInterface::class)
->expects($this->once())
->method('findOneByNameAndContext')
->willReturn($featureMock);
}
}
PHPUnit 8.2.5 by Sebastian Bergmann and contributors.
W 1 / 1 (100%)
Time: 46 ms, Memory: 6.00 MB
There was 1 warning:
1) Test::testOne
Method findOneByNameAndContext may not return value of type PHPUnit\Framework\MockObject\Builder\InvocationMocker, its return declaration is ": ?FeatureInterface"
The warning shown above is triggered because the createMock()
API is used incorrectly. Its return value must be stored in a variable. Then, on this variable, expects()
needs to be called:
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
interface FeatureInterface
{
public function isActive(): bool;
}
interface FeatureRepositoryInterface
{
public function findOneByNameAndContext(string $name, Context $context): ?FeatureInterface;
}
final class Test extends TestCase
{
public function testOne(): void
{
$featureMock = $this->createMock(FeatureInterface::class);
$featureMock->expects($this->once())
->method('isActive')
->willReturn(true);
$x = $this->createMock(FeatureRepositoryInterface::class);
$x->expects($this->once())
->method('findOneByNameAndContext')
->willReturn($featureMock);
}
}
PHPUnit 8.2.5 by Sebastian Bergmann and contributors.
F 1 / 1 (100%)
Time: 34 ms, Memory: 6.00 MB
There was 1 failure:
1) Test::testOne
Expectation failed for method name is equal to 'isActive' when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
Before you were passing the result of willReturn($expectedIsActive)
instead of the result of createMock()
to the second willReturn()
.
@sebastianbergmann you're right! I completely forgot about that behavior. Thank you!
Mocking a method with an
object
return type throws a warning, egMethod resolve may not return value of type object
. This was not happening in 8.1. For example, let's say I have this interface:And let's say I have the following (contrived) test:
I would expect this to pass, but instead I get a warning
Method resolve may not return value of type object
. I'm guessing it has something to do with this commit.composer info | sort
: