When writing tests intelephense gives error on PHPUnit MockObject.
Expected type 'PDO'. Found 'PHPUnit\Framework\MockObject\MockObject'.intelephense(10006)
Is there a way to get around this, or need to wait for #204?
It would be nice if you could provide example code which results in error, without that will be hard to find potential bugs.
I am mocking a PDO object so I can execute the logic test without actually hitting a database.
$statement = $this->getMockBuilder(\PDOStatement::class)
->setMethods(['execute'])
->getMock();
$statement->expects($this->once())
->method('execute')
->willReturn(false);
$pdo = $this->getMockBuilder(\PDO::class)
->disableOriginalConstructor()
->setMethods(['prepare'])
->getMock();
$pdo->method('prepare')->willReturn($statement);
$storage = new MyStorage\Pdo($pdo);
The MyStorage\Pdo class on the last line above expects a PDO object, which PHPUnit MockObject satisfies as far as php is concerned, but Intelephense complains.
getMock returns MockObject so you need to annotate the variable assignment to override this.
/** @var \PDO&\PHPUnit\Framework\MockObject\MockObject $pdo */
$pdo = $this->getMockBuilder(\PDO::class)
->disableOriginalConstructor()
->setMethods(['prepare'])
->getMock();
Thank you
Sorry to post here, not sure if it's worth opening a new ticket. My use case is quite similar but still having issues.
Long story short I was looking something like this to be picked by Intelephense but doesn't work:
// This annotation is ignored by Intelephense
/** @var \PDO&MockObject */
$this->pdo = $this->getMock(\PDO::class);
Please see below how this snipped is being used.
use PHPUnit\Framework\MockObject\MockObject;
class myClass extends TestCase
{
// This hinting doesn't make any difference
/** @var \PDO&MockObject */
private $pdo;
private $service;
protected function setUp()
{
// I've tried this but doesn't work either. Looks like
// Intelephense only looks at the return value of getMock()
/** @var \PDO&MockObject */
$this->pdo = $this->getMock(\PDO::class);
$this->someService = new Service(
// Intelephense complains here that pdo is a Mock and is not expected
$this->con
);
}
public function someTest()
{
// this is how pdo mock is used. That's why I promoted it to a class property
// instead of a local variable, I usually have many test methods that will make
// use of the mocked object.
$this->pdo->expects($this->once())...
$this->service->doSomethingThatDependsOnPDO();
}
Thank you!
I'm not using phpunit, so I've made up example code. However there is some kind of bug in handling override. @bmewburn can you take a look?
Edit: It is already included in phpunit https://github.com/sebastianbergmann/phpunit/blob/master/.phpstorm.meta.php and apparently doesn't work.
Mentioned example code:
<?php
namespace PHPSTORM_META{
override(\TestCase::getMock(0), map([
'' => '@&\MockObject',
]));
}
namespace {
class MockObject
{
/**
* Expects whatever
* @return stdClass
*/
public function expects(){}
}
class TestCase
{
public function getMock(){}
}
class myClass extends TestCase
{
public function someTest()
{
// here $pdo correctly type-hinted
$pdo = $this->getMock(\PDO::class);
// $me is mixed, and expects() have no hover
$me = $pdo->expects();
}
}
}
@KapitanOczywisty I've opened #968 for that.
@leftdevel I modified your example a little to remove other errors but I cannot reproduce the problem. $pdo is of the type in the annotation in my tests. Please open a new ticket with a minimal reproducible example if you are still having problems.
<?php
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
function expectsPdo(PDO $pdo) { }
class myClass extends TestCase
{
// This hinting doesn't make any difference
/** @var \PDO&MockObject */
private $pdo;
protected function setUp()
{
// I've tried this but doesn't work either. Looks like
// Intelephense only looks at the return value of getMock()
/** @var \PDO&MockObject */
$this->pdo = $this->getMock(\PDO::class);
expectsPdo($this->pdo);
}
public function someTest()
{
// this is how pdo mock is used. That's why I promoted it to a class property
// instead of a local variable, I usually have many test methods that will make
// use of the mocked object.
$this->pdo->expects($this->once())->
}
/** @return MockObject */
function getMock($name) {
return $this->createMock($name);
}
}

Hi @bmewburn, thanks so much for taking the time to check. I really appreciate it. I'll copy and paste your changes and see what's going on.
Hello. Sorry I am not sure if there is another issue here or not. I followed the additional comments above and other issues, not sure if what I am seeing is supposed to be fixed in 1.3.8 or is a new problem.
/** @var \PDO&\PHPUnit\Framework\MockObject\MockObject $pdo */
$pdo = $this->getMockBuilder(\PDO::class)
->disableOriginalConstructor()
->setMethods(['prepare'])
->getMock();
$pdo->expects($this->once());
The above code no longer works for me and gives Undefined method 'expects'.intelephense(1013) at version 1.3.11. Removing the \PDO& part makes the error go away, albeit also loosing code suggestions for removed object.
@mta59066 , I'm unable to reproduce this in 1.3.11 with the above snippet. Can you please add a complete example that reproduces the issue?
Thank you @bmewburn. The example code above is exactly what I am using to produce the issue, on a mac with vscode 1.42.1, php 7.4.2 and phpcs 3.5.4


Version: 1.42.1
Commit: c47d83b293181d9be64f27ff093689e8e7aed054
Date: 2020-02-11T14:44:27.652Z
Electron: 6.1.6
Chrome: 76.0.3809.146
Node.js: 12.4.0
V8: 7.6.303.31-electron.0
OS: Darwin x64 18.7.0
@mta59066 , I can't reproduce the issue with that code. Can you try reindexing? ctrl + shift + p -> Index workspace
@bmewburn Same after Index workspace. I downgraded version by version, it goes away on 1.3.6 and comes back on 1.3.7. I tried with or without premium license key, but that didn't make a difference.
@mta59066 what type is shown when you hover on $pdo on this line.
$pdo->expects($this->once());
@bmewburn It shows @var \PDO&\PHPUnit\Framework\MockObject\MockObject $pdo. Everything seems to work except the undefined method error.


@bmewburn is there any additional information I can provide on this?

So to get rid of the error message I have to create variables first?
Hello @bmewburn here is something that will hopefully allow you to replicate this problem.
Open a new file and just put the below code exactly as it is in it. Removing the testOther() method or even commenting out the $oth->other = true; line makes the problem go away. Just switched computers and created this on a new install.
<?php
class Test extends \PHPUnit\Framework\TestCase
{
public function testOther()
{
$oth = $this->getMockBuilder('Other')->getMock();
$oth->other = true;
}
function testProblem() {
/** @var \PDO&\PHPUnit\Framework\MockObject\MockObject $pdo */
$pdo = $this->getMockBuilder(\PDO::class)
->disableOriginalConstructor()
->setMethods(['prepare'])
->getMock();
$pdo->expects($this->once());
}
}
Here is what it looks like


Hello @bmewburn, can you relabel this issue as a bug if I have been able to provide you the necessary information to reproduce it?
@mta59066 , yes thanks for the example
Most helpful comment
getMockreturnsMockObjectso you need to annotate the variable assignment to override this.