psalm / psalm-plugin-mockery Goto Github PK
View Code? Open in Web Editor NEWA Mockery plugin for Psalm
License: MIT License
A Mockery plugin for Psalm
License: MIT License
Itβs in beta now: https://github.com/vimeo/psalm/releases/tag/5.0.0-beta1
Mockery provides syntax sugar for common return values:
andReturnSelf()
andReturnNull()
andReturnFalse()
andReturnTrue()
Please add this to the psalm plugin, in order to better support mockery.
Hi,
It seems that when the plugin is active some errors are not visible, for example with the following test class I would have expected to get an InvalidReturnType
and an InvalidReturnStatement
errors but instead no errors are thrown:
<?php
declare(strict_types=1);
namespace Repro;
use PHPUnit\Framework\TestCase;
final class ReproTest extends TestCase
{
use \Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
public function dataProviderCoveringScopes(): bool
{
return ['foo'];
}
}
I have set up a repo that can be used to reproduce: https://github.com/LeSuisse/repro-issue-psalm-mockery-issue-no-error
When I call shouldReceive
method on a mocked class I get the UndefinedMethod error.
I have already seen this issue but it is not clear to me how to solve
@muglug need a hand maintaining this repo?
I'm getting strange behaviour trying to use a spy.
Let me show an example:
/**
* @psalm-suppress MissingConstructor
*/
class SpyTest extends TestCase
{
/**
* @test
*/
public function spy(): void
{
$mockDepService = \Mockery::mock(DepService::class);
$s1 = new Service($mockDepService);
$spyDepService = \Mockery::spy(DepService::class);
$s2 = new Service($spyDepService);
}
}
class DepService
{
}
class Service
{
public function __construct(private DepService $depService)
{
}
}
By running Psalm on this code I get an error on the spy but not on the mock.
ERROR: InvalidArgument - tests/SpyTest.php:23:27 - Argument 1 of Tests\Service::__construct expects Tests\DepService, Mockery\MockInterface provided (see https://psalm.dev/004)
$s2 = new Service($spyDepService);
Am I doing something wrong?
That started happening in the last week with unchanged analyzed code.
Code that makes it crash:
$session = Mockery::mock(\Mezzio\Session\SessionInterface::class);
Changing this for the phpunit mock fixes the issue.
Stack trace:
Uncaught Error: Call to undefined method Psalm\Type\Union::getTypes() in /Volumes/Project/example-project/vendor/psalm/plugin-mockery/Hooks/MockReturnTypeUpdater.php:71
Stack trace:
#0 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php(480): Psalm\MockeryPlugin\Hooks\MockReturnTypeUpdater::afterMethodCallAnalysis(Object(PhpParser\Node\Expr\StaticCall), 'Mockery::mock', 'Mockery::mock', 'Mockery::mock', Object(Psalm\Context), Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Codebase), Array, Object(Psalm\Type\Union))
#1 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php(721): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\ExistingAtomicStaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(PhpParser\Node\Identifier), Array, Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Object(Psalm\Internal\MethodIdentifier), 'Mockery::mock', Object(Psalm\Storage\ClassLikeStorage), false)
#2 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php(171): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer::handleNamedCall(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(PhpParser\Node\Identifier), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Array, 'Mockery', false, true)
#3 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php(205): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), false, false, false, true)
#4 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(155): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context))
#5 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(39): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), false, NULL, false)
#6 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php(197): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context))
#7 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php(306): Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentsAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Array, Array, 'D4H\\Decisions\\S...', true, Object(Psalm\Context), Object(Psalm\Internal\Type\TemplateResult))
#8 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php(324): Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer::checkMethodArgs(Object(Psalm\Internal\MethodIdentifier), Array, Object(Psalm\Internal\Type\TemplateResult), Object(Psalm\Context), Object(Psalm\CodeLocation), Object(Psalm\Internal\Analyzer\StatementsAnalyzer))
#9 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php(204): Psalm\Internal\Analyzer\Statements\Expression\Call\NewAnalyzer::analyzeNamedConstructor(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(Psalm\Codebase), Object(PhpParser\Node\Expr\New_), Object(Psalm\Context), 'D4H\\Decisions\\S...', false, false)
#10 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(250): Psalm\Internal\Analyzer\Statements\Expression\Call\NewAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\New_), Object(Psalm\Context))
#11 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(39): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\New_), Object(Psalm\Context), false, NULL, false)
#12 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php(194): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\New_), Object(Psalm\Context))
#13 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(132): Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\Variable), Object(PhpParser\Node\Expr\New_), NULL, Object(Psalm\Context), NULL)
#14 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(39): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\Assign), Object(Psalm\Context), false, Object(Psalm\Context), true)
#15 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(522): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\Assign), Object(Psalm\Context), false, Object(Psalm\Context), true)
#16 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(171): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Expression), Object(Psalm\Context), Object(Psalm\Context))
#17 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(654): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), Object(Psalm\Context), true)
#18 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1978): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), Object(Psalm\Context))
#19 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1646): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeClassMethod(Object(PhpParser\Node\Stmt\ClassMethod), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Internal\Analyzer\TraitAnalyzer), Object(Psalm\Context), Object(Psalm\Context))
#20 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(758): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeTraitUse(Object(Psalm\Aliases), Object(PhpParser\Node\Stmt\TraitUse), Object(Psalm\Internal\Analyzer\ProjectAnalyzer), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Context), Object(Psalm\Context), NULL)
#21 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(211): Psalm\Internal\Analyzer\ClassAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Context))
#22 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(340): Psalm\Internal\Analyzer\FileAnalyzer->analyze(NULL)
#23 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(579): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}(91, '/Volumes/Source...')
#24 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(269): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1)
#25 /Users/foo/.composer/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(1182): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1, false, false)
#26 /Users/foo/.composer/vendor/vimeo/psalm/src/psalm.php(678): Psalm\Internal\Analyzer\ProjectAnalyzer->checkPaths(Array)
#27 /Users/foo/.composer/vendor/vimeo/psalm/psalm(2): require_once('/Users/foo/.co...')
#28 {main}
(Psalm 4.3.1@2feba22a005a18bf31d4c7b9bdb9252c73897476 crashed due to an uncaught Throwable)
I have a project that uses Mockery, and have both phpstan and psalm running against it.
I'm using this plugin and the phpstan mockery plugin (https://github.com/phpstan/phpstan-mockery) to help reduce the number of errors that have to ignored and/or worked around.
One thing that the phpstan plugin does, that this plugin doesn't seem to, is treating the Mockery\MockInterface
as an intersection type when defined with another object:
Interprets Foo|\Mockery\MockInterface in phpDoc so that it results in an intersection type instead of a union type.
This allows me to do something like this:
/** @var User|\Mockery\MockInterface $user */
$user = Mockery::mock(
'User[oneMethod,twoMethod]',
$constructorParams
);
When defining mocks, and then the methods available on both User
and \Mockery\MockInterface
are available. So when the Mock is later used like so:
$user->shouldReceive('oneMethod')->zeroOrMoreTimes();
phpstan won't complain that the User
class doesn't have the shouldReceive
method. Likewise, this code also won't trigger an error:
$user->oneMethod()
Since the User
object does have this method (but \Mockery\MockInterface
) does not.
Is this something that could/should be added to this plugin? I can try to take a stab at it if so...
When installing this package with composer install -o
I get a notice that there are 2 classes that do not comply with PSR-4 and will not work with Composer 2.0.
Deprecation Notice: Class Psalm\MockeryPlugin\Tests\AcceptanceTester located in ./vendor/psalm/plugin-mockery/tests/_support/AcceptanceTester.php does not comply with psr-4 autoloading standard. It will not autoload anymore in Composer v2.0. in phar:///usr/local/bin/composer/src/Composer/Autoload/ClassMapGenerator.php:201
Deprecation Notice: Class Psalm\MockeryPlugin\Tests\Helper\Acceptance located in ./vendor/psalm/plugin-mockery/tests/_support/Helper/Acceptance.php does not comply with psr-4 autoloading standard. It will not autoload anymore in Composer v2.0. in phar:///usr/local/bin/composer/src/Composer/Autoload/ClassMapGenerator.php:201
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.