phpstan / phpstan-mockery Goto Github PK
View Code? Open in Web Editor NEWPHPStan extension for Mockery
PHPStan extension for Mockery
I have a ParentTestCase that sets up a few common variables, and these are then failing in PHPStan with the message:
24 Call to an undefined method Mockery\LegacyMockInterface|Psr\Log\LoggerInterface::shouldReceive().
ActionTestCase.php
<?php
declare(strict_types=1);
namespace Tests\Action;
use DI\ContainerBuilder;
use Exception;
use Mockery;
use Mockery\Adapter\Phpunit\MockeryTestCase;
use Mockery\LegacyMockInterface;
use Mockery\MockInterface;
use Psr\Log\LoggerInterface;
use Slim\App;
use Slim\Factory\AppFactory;
class ActionTestCase extends MockeryTestCase
{
/**
* @var LoggerInterface|LegacyMockInterface|MockInterface
*/
protected $logger;
protected function setUp(): void
{
parent::setUp();
$this->logger = Mockery::mock(LoggerInterface::class);
}
/**
* @return App
* @throws Exception
*/
protected function getAppInstance(): App
{
// Instantiate PHP-DI ContainerBuilder
$containerBuilder = new ContainerBuilder();
// Set up settings
$settings = require __DIR__ . '/../../app/settings.php';
$settings($containerBuilder);
// Set up dependencies
$dependencies = require __DIR__ . '/../../app/dependencies.php';
$dependencies($containerBuilder);
// Set up repositories
$repositories = require __DIR__ . '/../../app/repositories.php';
$repositories($containerBuilder);
// Build PHP-DI Container instance
$container = $containerBuilder->build();
$container->set(LoggerInterface::class, $this->logger);
// Instantiate the app
AppFactory::setContainer($container);
$app = AppFactory::create();
// Register routes
$routes = require __DIR__ . '/../../app/routes.php';
$routes($app);
$app->addBodyParsingMiddleware();
$app->addRoutingMiddleware();
return $app;
}
}
AuthLinkActionTest.php
<?php
declare(strict_types=1);
namespace Tests\Action\Auth;
use Tests\Action\ActionTestCase;
class AuthLinkActionTest extends ActionTestCase
{
/**
* @throws Exception
*/
public function testLinkNoParamsLogsAndRedirects(): void
{
$app = $this->getAppInstance();
$this->logger->shouldReceive('critical')->once()
->with('Authentication error: Could not link client to application')
->andReturnSelf();
$request = $this->createRequest('GET', '/auth/link');
$response = $app->handle($request);
self::assertEquals(302, $response->getStatusCode());
self::assertEquals('/', $response->getHeaderLine('Location'));
}
}
The same thing happens if I move the setUp function to the AuthLinkActionTest class.
I have found if I do the following, it works, but that defeats the point of having a "common" variable
<?php
declare(strict_types=1);
namespace Tests\Action\Auth;
use Tests\Action\ActionTestCase;
class AuthLinkActionTest extends ActionTestCase
{
/**
* @throws Exception
*/
public function testLinkNoParamsLogsAndRedirects(): void
{
$app = $this->getAppInstance();
$this->logger = Mockery::mock(LoggerInterface::class);
$this->logger->shouldReceive('critical')->once()
->with('Authentication error: Could not link client to application')
->andReturnSelf();
$request = $this->createRequest('GET', '/auth/link');
$response = $app->handle($request);
self::assertEquals(302, $response->getStatusCode());
self::assertEquals('/', $response->getHeaderLine('Location'));
}
}
$mock = Mockery::mock(Something::class);
$mock
->shouldReceive('foo')
->andReturn(null)
->shouldReceive('bar');
Currently, the second shouldReceive
call will trigger this error: Call to an undefined method Mockery\Expectation::shouldReceive().
$mock = Mockery::mock('Foo\Bar[method1,method2]');
Currently, $mock is assumed to be of type Foo\Bar[method1,method2]
$mock = Mockery::mock(Bar::class)->shouldDeferMissing();
Currently, methods returning Mockery\Mock will just return that, instead of an intersection type.
I noticed the master branch is prepared to use phpstan 0.12. Any update on when we can expect a release so that we can use this with the newest version of phpstan?
https://phpstan.org/r/4d0416f7-0b46-471e-b961-a685a5fab277
Current versions report an error
Parameter #1 $callback of static method Closure::fromCallable() expects callable(): mixed, Mockery\MockInterface given.
$mock = Mockery::mock(MyClass::class);
self::assertSth($mock);
...
private function assertSth(MockInterface $mock) : void
{
$mock->expect('call')->once();
}
Gives
Call to an undefined method Mockery\ExpectationInterface|Mockery\ExpectsHigherOrderMessage::once()
v0.11.3
Not sure if doable ๐ค
Mockery::mock()
returns a Mockery\MockInterface
. MockInterface::shouldReceive()
et al returns an Mockery\Expectation
, which in turn has a getMock()
method to return the MockInterface
the Expectation
belongs to.
The problem is that phpstan says the return type of Expectation::getMock()
is Mockery\MockInterface
with no type information on what was originally mocked. So for example
interface A {
foo(): void
}
function getMock(): A
{
return Mockery::mock(A::class)
->shouldReceive('foo')
->getMock();
}
will cause the following phpstan error: Function getMock() should return A but returns Mockery\MockInterface.
Of course the code in getMock()
can be rewritten to not be fluent so that the return value of Mockery::mock()
is returned instead, however in many cases I mock objects inline in some other expression and when I do that it's really helpful to be able to use the fluent way of writing the expectations.
Is it possible to change the type of the return value of Mockery\Expectation::getMock()
to include the type information of the original mock? With generics I guess it should be something like Mockery::mock(T): MockInterface<T>
, MockInterface<T>::shouldReceive(): Expectation<T>
and Expectation<T>::getMock(): MockInterface<T>
.
Are there any other needed changes then bumping the version for making this this extension compatible with the latest PHPStan develop version? Could I help out with the process some how?
Line 20 in 8f3f0dc
It is possible to create a partial mock like this:
class SomeTest extends \PHPUnit\Framework\TestCase {
public function createMock(): MyClass
{
return Mockery::mock(MyClass::class)->makePartial();
}
}
However, phpstan will complain that Method SomeTest::createMock() should return MyClass but returns Mockery\Mock.
.
The doc blocks for the shouldHaveReceived
and shouldNotHaveReceived
methods in
phpstan-mockery/stubs/MockInterface.stub
Line 47 in 245b17c
These methods can also accept a Closure
as the second argument, as you can see here: https://github.com/mockery/mockery/blob/master/library/Mockery/LegacyMockInterface.php#L87.
Currently, providing a closure as the second argument will trigger a PHPStan error.
Parameter #2 $args of method Mockery\LegacyMockInterface::shouldNotHaveReceived() expects array<string>|null, Closure given.
similar to #3
I am still experiencing the following code as failing on 0.11.2
$mock = Mockery::mock(Something::class);
$mock
->shouldReceive('foo')
->andReturn(null)
->shouldReceive('bar');
Currently, the second shouldReceive call will trigger this error: Call to an undefined method Mockery\Expectation::shouldReceive().
class Foo
{
protected function bar()
{
echo 'bar';
}
}
$mock = \Mockery::mock(Foo::class);
$mock
->shouldAllowMockingProtectedMethods()
->makePartial();
$mock->bar();
Call to protected method bar()
phpstan/phpstan v0.12.25
phpstan/phpstan-mockery v0.12.5
When upgrading from phpstan/phpstan 0.12.88 to 0.12.89 I'm getting a lot of errors like this:
Calling PHPStan\Mockery\Type\Expects::with() is not covered by backward compatibility promise. The
method might change in a minor PHPStan version.
๐ก If you think it should be covered by backward compatibility promise, open a discussion:
https://github.com/phpstan/phpstan/discussions
See also:
https://phpstan.org/developing-extensions/backward-compatibility-promise
I see that a fix has been pushed for phpstan-phpunit
. Should the same type of fix be done here as well?
$mock = Mockery::mock(MyClass::class);
$expectCall = $mock->expect('call');
self::assertSth($expectCall);
...
private function assertSth(Expectation $expectCall) : void
{
$expectCall->once();
}
Gives
Parameter #1 $expectCall of method Ns\ClassTest::assertSth() expects Mockery\Expectation, PHPStan\Mockery\Type\Expects given.
v0.11.3
I'm having this test:
use Mockery\MockInterface;
class TypedPropertyTest extends \PHPUnit\Framework\TestCase
{
private MockInterface $fooMock;
protected function setUp(): void
{
$this->fooMock = \Mockery::mock(Foo::class);
}
public function testAllows(): void
{
$bar = new Bar($this->fooMock);
$this->fooMock
->allows('doFoo')
->with()
->andReturn('foo');
self::assertSame('foo', $bar->doFoo());
}
}
But PHPStan reports issues:
------ ----------------------------------------------------------------------------------------------------------------------
Line tests/Mockery/TypedPropertyTest.php
------ ----------------------------------------------------------------------------------------------------------------------
19 Parameter #1 $foo of class PHPStan\Mockery\Bar constructor expects PHPStan\Mockery\Foo, Mockery\MockInterface given.
21 Call to an undefined method Mockery\ExpectationInterface|Mockery\HigherOrderMessage|Mockery\MockInterface::with().
------ ----------------------------------------------------------------------------------------------------------------------
The only way to solve this is by adding a PHPDoc:
/** @var MockInterface|Foo */
private MockInterface $fooMock;
It would be great if it could read the Foo
type from the setUp
method and intersect that with MockInterface
type.
@ondrejmirtes Is that possible? Do you maybe have an example that's similar? I'd like to create a PR to address it.
phpstan/phpstan-mockery
0.12.7
phpstan/phpstan-phpunit
0.12.16
# phpstan.neon.dist
includes:
- ./vendor/phpstan/phpstan-mockery/extension.neon
- ./vendor/phpstan/phpstan-phpunit/extension.neon
Using the most basic shouldReceive
syntax produces no PHPStan errors.
/** @var MyClass|MockInterface */
$myMock = Mockery::mock(MyClass::class);
$myMock
->shouldReceive('someMethod')
->andReturn('resultString');
Using the array notation for shouldReceive
gives the following error:
Parameter #1 ...$methodNames of method Mockery\LegacyMockInterface::shouldReceive() expects string, array<string, string> given.
/** @var MyClass|MockInterface */
$myMock = Mockery::mock(MyClass::class);
$myMock->shouldReceive([
'someMethod' => 'resultString',
]);
http://docs.mockery.io/en/latest/reference/expectations.html
When using Mockery in your PHPUnit tests, it's important to make sure you call Mockery::close()
on tearDown()
. Or use the MockeryPHPUnitIntegration
trait for that.
This is often forgotten, and that leads to weird situations.
It would be great if the extension could detect this, and give an error instructing you what to do to solve it.
Some people might extend from a custom base class where this is done for them, for those this feature should be turned off, or configured so that it also supports a custom base class.
The offending code looks like this:
$gitlab = $this->mock(GitLabManager::class);
$gitlab
->expects('repositories')
->never();
Reformatting does not change the error:
$gitlab = $this->mock(GitLabManager::class);
$gitlab->expects('repositories')->never();
My dependencies are the following:
"mockery/mockery": "^1.3.1",
"phpstan/phpstan-mockery": "^0.12.3",
"nunomaduro/larastan": "^0.5.0",
Am i doing something wrong?
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
nikic/php-parser
, phpunit/phpunit
)composer.json
php ^7.2 || ^8.0
phpstan/phpstan ^1.10
mockery/mockery ^1.6.11
nikic/php-parser ^4.13.0
php-parallel-lint/php-parallel-lint ^1.2
phpstan/phpstan-phpunit ^1.0
phpstan/phpstan-strict-rules ^1.0
phpunit/phpunit ^9.5
.github/workflows/build.yml
actions/checkout v4
shivammathur/setup-php v2
actions/checkout v4
actions/checkout v4
shivammathur/setup-php v2
actions/checkout v4
shivammathur/setup-php v2
actions/checkout v4
shivammathur/setup-php v2
.github/workflows/create-tag.yml
actions/checkout v4
WyriHaximus/github-action-get-previous-tag v1
WyriHaximus/github-action-next-semvers v1
rickstaa/action-create-tag v1
rickstaa/action-create-tag v1
.github/workflows/lock-closed-issues.yml
dessant/lock-threads v5
.github/workflows/release-toot.yml
cbrgm/mastodon-github-action v2
.github/workflows/release-tweet.yml
Eomm/why-don-t-you-tweet v1
.github/workflows/release.yml
actions/checkout v4
metcalfc/changelog-generator v4.3.1
actions/create-release v1
Hi
I am using PHPStan version 0.12.2 and phpstan/phpstan-mockery version 0.12.3.
My code looks like this:
$validatorMock = \Mockery::spy('alias:' . Validator::class);
$validatorMock->shouldReceive(['isValueValid' => false]);
I am mocking Validator class, which has static method isValueValid.
And PHPStan reports error:
Parameter #1 ...$methodNames of method Mockery\LegacyMockInterface::shouldReceive() expects string, array<string, false> given.
However when I change the syntax to
$validatorMock->shouldReceive('isValueValid')->andReturn(false);
PHPStan does not report an error then. Must be this problem fixed in this package or not?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.