phpstan / phpstan-phpunit

PHPUnit extensions and rules for PHPStan
MIT License
460 stars 45 forks source link

Phpstan not recognizing mockobjects #85

Closed t-heuser closed 3 years ago

t-heuser commented 3 years ago

Hi, I just tried to setup phpstan with phpunit but it's not working for me. I have installed phpstan/phpstan@0.12.50, phpstan/phpstan-phpunit@0.12.16 and phpstan/phpstan/extension-loader@1.0.5. Here I have a minimum (not) working example:

<?php

declare(strict_types=1);

namespace Tests;

use PHPUnit\Framework\MockObject\MockObject;

/**
 * Class TestTestCase
 */
final class TestTestCase extends AbstractTestCase
{
    /** @var MockObject|TestConstr */
    protected $mock;

    protected function setUp(): void
    {
        parent::setUp();

        $this->mock = $this->createMock(TestConstr::class);
    }

    public function testTest(): void
    {
        $this->mock->expects(self::once())->method('test');
    }
}

The TestConstr class is just an empty final class. When I execute phpstan now at level 7 I get the following error: 26 Call to an undefined method PHPUnit\Framework\MockObject\MockObject|Tests\TestConstr::expects().

This should not be the case with this package or am I missing something?

ondrejmirtes commented 3 years ago

Hi, can you show your composer.json and the output of running fresh composer install?

Otherwise I'll have to ask your to create a small reproducing repository since this use-case definitely works and is tested.

BTW the correct way to annotate a mock is MockObject&TestConstr. MockObject|TestConstr also works to construct an intersection type but that's thanks to a special exception in this extension. See more: https://phpstan.org/blog/union-types-vs-intersection-types

t-heuser commented 3 years ago

Hi, I know about the way to annotate it with "&". But If I do that I get another error: PHPDoc tag @var for property Tests\Unit\Model\Session\SessionTest::$soapHelperMock contains unresolvable type. This is the property with it's PHPDoc tag to which throws the error:

    /** @var MockObject&SoapHelper */
    protected $soapHelperMock;

So this isn't working either. The SoapHelper class is definitly existing and also discovered by phpstan as the file is within the analysed path.

Here is my composer.json:

composer.json ````JSON { "name": "oneserv/ofml", "type": "project", "description": "OFML GraphQL Api.", "require": { "php": "^7.3", "ext-json": "*", "ext-soap": "*", "fideloper/proxy": "^4.4.0", "fruitcake/laravel-cors": "^2.0.2", "guzzlehttp/guzzle": "^7.1.1", "laravel/framework": "^8.7.1", "laravel/tinker": "^2.4.2", "myclabs/php-enum": "^1.7.6", "predis/predis": "^1.1.6", "thecodingmachine/graphqlite-laravel": "^4.0.3" }, "require-dev": { "barryvdh/laravel-ide-helper": "^2.8.1", "dg/bypass-finals": "^1.3.0", "ergebnis/phpstan-rules": "^0.15.2", "facade/ignition": "^2.3.7", "fzaninotto/faker": "^1.9.1", "mockery/mockery": "^1.4.2", "nunomaduro/collision": "^5.0.2", "nunomaduro/larastan": "^0.6.8", "phpmd/phpmd": "^2.9.1", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^0.12.50", "phpstan/phpstan-deprecation-rules": "^0.12.5", "phpstan/phpstan-phpunit": "^0.12.16", "phpstan/phpstan-strict-rules": "^0.12.5", "phpunit/phpunit": "^9.3.11", "squizlabs/php_codesniffer": "^3.5.6" }, "config": { "optimize-autoloader": true, "preferred-install": "dist", "sort-packages": true }, "extra": { "laravel": { "dont-discover": [] } }, "autoload": { "psr-4": { "App\\": "app/" } }, "autoload-dev": { "psr-4": { "Tests\\": "tests/" } }, "minimum-stability": "stable", "prefer-stable": true, "scripts": { "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "@php artisan package:discover --ansi" ], "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], "post-create-project-cmd": [ "@php artisan key:generate --ansi" ], "post-install-cmd": [ "[ $COMPOSER_DEV_MODE -eq 1 ] && vendor/bin/phpcs --config-set installed_paths ./../../../dev/phpcs/ && vendor/bin/phpcs --config-set default_standard Oneserv || echo 0" ], "post-update-cmd": [ "[ $COMPOSER_DEV_MODE -eq 1 ] && vendor/bin/phpcs --config-set installed_paths ./../../../dev/phpcs/ && vendor/bin/phpcs --config-set default_standard Oneserv || echo 0" ] } } ````

Here is the output of a fresh composer install:

composer install output Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Package operations: 156 installs, 0 updates, 0 removals - Installing ocramius/package-versions (1.5.1): Loading from cache - Installing phpstan/phpstan (0.12.50): Loading from cache - Installing phpstan/extension-installer (1.0.5): Loading from cache - Installing doctrine/lexer (1.2.1): Loading from cache - Installing doctrine/annotations (1.10.4): Loading from cache - Installing doctrine/inflector (2.0.3): Loading from cache - Installing dragonmantank/cron-expression (v3.0.2): Loading from cache - Installing webonyx/graphql-php (v0.13.9): Loading from cache - Installing psr/http-message (1.0.1): Loading from cache - Installing psr/http-server-handler (1.0.1): Loading from cache - Installing psr/http-server-middleware (1.0.1): Loading from cache - Installing ecodev/graphql-upload (4.1.0): Loading from cache - Installing voku/portable-ascii (1.5.3): Loading from cache - Installing symfony/polyfill-php80 (v1.18.1): Loading from cache - Installing symfony/polyfill-mbstring (v1.18.1): Loading from cache - Installing symfony/polyfill-ctype (v1.18.1): Loading from cache - Installing phpoption/phpoption (1.7.5): Loading from cache - Installing graham-campbell/result-type (v1.0.1): Loading from cache - Installing vlucas/phpdotenv (v5.2.0): Loading from cache - Installing symfony/css-selector (v5.1.7): Loading from cache - Installing tijsverkoyen/css-to-inline-styles (2.2.3): Loading from cache - Installing symfony/var-dumper (v5.1.7): Loading from cache - Installing symfony/deprecation-contracts (v2.2.0): Loading from cache - Installing symfony/routing (v5.1.7): Loading from cache - Installing symfony/process (v5.1.7): Loading from cache - Installing symfony/polyfill-php72 (v1.18.1): Loading from cache - Installing paragonie/random_compat (v9.99.99): Loading from cache - Installing symfony/polyfill-php70 (v1.18.1): Loading from cache - Installing symfony/polyfill-intl-normalizer (v1.18.1): Loading from cache - Installing symfony/polyfill-intl-idn (v1.18.1): Loading from cache - Installing symfony/mime (v5.1.7): Loading from cache - Installing symfony/polyfill-php73 (v1.18.1): Loading from cache - Installing symfony/http-foundation (v5.1.7): Loading from cache - Installing symfony/http-client-contracts (v2.2.0): Loading from cache - Installing psr/event-dispatcher (1.0.0): Loading from cache - Installing symfony/event-dispatcher-contracts (v2.2.0): Loading from cache - Installing symfony/event-dispatcher (v5.1.7): Loading from cache - Installing psr/log (1.1.3): Loading from cache - Installing symfony/error-handler (v5.1.7): Loading from cache - Installing symfony/http-kernel (v5.1.7): Loading from cache - Installing symfony/finder (v5.1.7): Loading from cache - Installing symfony/polyfill-intl-grapheme (v1.18.1): Loading from cache - Installing symfony/string (v5.1.7): Loading from cache - Installing psr/container (1.0.0): Loading from cache - Installing symfony/service-contracts (v2.2.0): Loading from cache - Installing symfony/console (v5.1.7): Loading from cache - Installing symfony/polyfill-iconv (v1.18.1): Loading from cache - Installing egulias/email-validator (2.1.22): Loading from cache - Installing swiftmailer/swiftmailer (v6.2.3): Loading from cache - Installing ramsey/collection (1.1.1): Loading from cache - Installing brick/math (0.9.1): Loading from cache - Installing ramsey/uuid (4.1.1): Loading from cache - Installing psr/simple-cache (1.0.1): Loading from cache - Installing opis/closure (3.6.0): Loading from cache - Installing symfony/translation-contracts (v2.3.0): Loading from cache - Installing symfony/translation (v5.1.7): Loading from cache - Installing nesbot/carbon (2.41.3): Loading from cache - Installing monolog/monolog (2.1.1): Loading from cache - Installing league/mime-type-detection (1.5.0): Loading from cache - Installing league/flysystem (1.1.3): Loading from cache - Installing league/commonmark (1.5.5): Loading from cache - Installing laravel/framework (v8.10.0): Loading from cache - Installing fideloper/proxy (4.4.0): Loading from cache - Installing asm89/stack-cors (v2.0.1): Loading from cache - Installing fruitcake/laravel-cors (v2.0.2): Loading from cache - Installing psr/http-client (1.0.1): Loading from cache - Installing ralouphie/getallheaders (3.0.3): Loading from cache - Installing guzzlehttp/psr7 (1.7.0): Loading from cache - Installing guzzlehttp/promises (1.4.0): Loading from cache - Installing guzzlehttp/guzzle (7.2.0): Loading from cache - Installing laminas/laminas-zendframework-bridge (1.1.1): Loading from cache - Installing nikic/php-parser (v4.10.2): Loading from cache - Installing dnoegel/php-xdg-base-dir (v0.1.1): Loading from cache - Installing psy/psysh (v0.10.4): Loading from cache - Installing laravel/tinker (v2.4.2): Loading from cache - Installing myclabs/php-enum (1.7.6): Loading from cache - Installing webmozart/assert (1.9.1): Loading from cache - Installing phpdocumentor/reflection-common (2.2.0): Loading from cache - Installing phpdocumentor/type-resolver (1.4.0): Loading from cache - Installing phpdocumentor/reflection-docblock (5.2.2): Loading from cache - Installing predis/predis (v1.1.6): Loading from cache - Installing psr/http-factory (1.0.1): Loading from cache - Installing psr/cache (1.0.1): Loading from cache - Installing symfony/cache-contracts (v2.2.0): Loading from cache - Installing symfony/var-exporter (v5.1.7): Loading from cache - Installing symfony/cache (v5.1.7): Loading from cache - Installing symfony/expression-language (v5.1.7): Loading from cache - Installing thecodingmachine/cache-utils (v1.0.0): Loading from cache - Installing mouf/classname-mapper (v1.0.2): Loading from cache - Installing thecodingmachine/class-explorer (v1.1.0): Loading from cache - Installing doctrine/cache (1.10.2): Loading from cache - Installing thecodingmachine/graphqlite (v4.0.3): Loading from cache - Installing symfony/psr-http-message-bridge (v2.0.2): Loading from cache - Installing laminas/laminas-diactoros (2.4.1): Loading from cache - Installing thecodingmachine/graphqlite-laravel (v4.0.3): Loading from cache - Installing doctrine/event-manager (1.1.1): Loading from cache - Installing doctrine/dbal (2.11.1): Loading from cache - Installing symfony/filesystem (v5.1.7): Loading from cache - Installing seld/phar-utils (1.1.1): Loading from cache - Installing seld/jsonlint (1.8.2): Loading from cache - Installing justinrainbow/json-schema (5.2.10): Loading from cache - Installing composer/xdebug-handler (1.4.3): Loading from cache - Installing composer/spdx-licenses (1.5.4): Loading from cache - Installing composer/semver (1.7.1): Loading from cache - Installing composer/ca-bundle (1.2.8): Loading from cache - Installing composer/composer (1.10.15): Loading from cache - Installing barryvdh/reflection-docblock (v2.0.6): Loading from cache - Installing barryvdh/laravel-ide-helper (v2.8.1): Loading from cache - Installing dg/bypass-finals (v1.3.0): Loading from cache - Installing ergebnis/phpstan-rules (0.15.2): Loading from cache - Installing scrivo/highlight.php (v9.18.1.2): Loading from cache - Installing filp/whoops (2.7.3): Loading from cache - Installing facade/ignition-contracts (1.0.1): Loading from cache - Installing facade/flare-client-php (1.3.6): Loading from cache - Installing facade/ignition (2.4.1): Loading from cache - Installing fzaninotto/faker (v1.9.1): Loading from cache - Installing hamcrest/hamcrest-php (v2.0.1): Loading from cache - Installing nunomaduro/collision (v5.0.2): Loading from cache - Installing mockery/mockery (1.4.2): Loading from cache - Installing nunomaduro/larastan (v0.6.8): Loading from cache - Installing symfony/dependency-injection (v5.1.7): Loading from cache - Installing symfony/config (v5.1.7): Loading from cache - Installing pdepend/pdepend (2.8.0): Loading from cache - Installing phpmd/phpmd (2.9.1): Loading from cache - Installing phpstan/phpstan-deprecation-rules (0.12.5): Loading from cache - Installing phpstan/phpstan-phpunit (0.12.16): Loading from cache - Installing phpstan/phpstan-strict-rules (0.12.5): Loading from cache - Installing sebastian/version (3.0.2): Loading from cache - Installing sebastian/type (2.3.0): Loading from cache - Installing sebastian/resource-operations (3.0.3): Loading from cache - Installing sebastian/recursion-context (4.0.3): Loading from cache - Installing sebastian/object-reflector (2.0.3): Loading from cache - Installing sebastian/object-enumerator (4.0.3): Loading from cache - Installing sebastian/global-state (5.0.1): Loading from cache - Installing sebastian/exporter (4.0.3): Loading from cache - Installing sebastian/environment (5.1.3): Loading from cache - Installing sebastian/diff (4.0.3): Loading from cache - Installing sebastian/comparator (4.0.5): Loading from cache - Installing sebastian/code-unit (1.0.7): Loading from cache - Installing sebastian/cli-parser (1.0.1): Loading from cache - Installing phpunit/php-timer (5.0.2): Loading from cache - Installing phpunit/php-text-template (2.0.3): Loading from cache - Installing phpunit/php-invoker (3.1.1): Loading from cache - Installing phpunit/php-file-iterator (3.0.5): Loading from cache - Installing theseer/tokenizer (1.2.0): Loading from cache - Installing sebastian/lines-of-code (1.0.1): Loading from cache - Installing sebastian/complexity (2.0.1): Loading from cache - Installing sebastian/code-unit-reverse-lookup (2.0.3): Loading from cache - Installing phpunit/php-code-coverage (9.2.0): Loading from cache - Installing doctrine/instantiator (1.3.1): Loading from cache - Installing phpspec/prophecy (1.12.1): Loading from cache - Installing phar-io/version (3.0.2): Loading from cache - Installing phar-io/manifest (2.0.1): Loading from cache - Installing myclabs/deep-copy (1.10.1): Loading from cache - Installing phpunit/phpunit (9.4.1): Loading from cache - Installing squizlabs/php_codesniffer (3.5.6): Loading from cache webonyx/graphql-php suggests installing react/promise (To leverage async resolving on React PHP platform) symfony/routing suggests installing symfony/yaml (For using the YAML loader) paragonie/random_compat suggests installing ext-libsodium (Provides a modern crypto API that can be used to generate random bytes.) symfony/http-client-contracts suggests installing symfony/http-client-implementation symfony/http-kernel suggests installing symfony/browser-kit symfony/console suggests installing symfony/lock swiftmailer/swiftmailer suggests installing true/punycode (Needed to support internationalized email addresses, if ext-intl is not installed) ramsey/uuid suggests installing ext-gmp (Enables faster math with arbitrary-precision integers using GMP.) ramsey/uuid suggests installing ext-uuid (Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.) ramsey/uuid suggests installing paragonie/random-lib (Provides RandomLib for use with the RandomLibAdapter) ramsey/uuid suggests installing ramsey/uuid-doctrine (Allows the use of Ramsey\Uuid\Uuid as Doctrine field type.) symfony/translation suggests installing symfony/yaml monolog/monolog suggests installing aws/aws-sdk-php (Allow sending log messages to AWS services like DynamoDB) monolog/monolog suggests installing doctrine/couchdb (Allow sending log messages to a CouchDB server) monolog/monolog suggests installing elasticsearch/elasticsearch (Allow sending log messages to an Elasticsearch server via official client) monolog/monolog suggests installing ext-amqp (Allow sending log messages to an AMQP server (1.0+ required)) monolog/monolog suggests installing ext-mongodb (Allow sending log messages to a MongoDB server (via driver)) monolog/monolog suggests installing graylog2/gelf-php (Allow sending log messages to a GrayLog2 server) monolog/monolog suggests installing mongodb/mongodb (Allow sending log messages to a MongoDB server (via library)) monolog/monolog suggests installing php-amqplib/php-amqplib (Allow sending log messages to an AMQP server using php-amqplib) monolog/monolog suggests installing php-console/php-console (Allow sending log messages to Google Chrome) monolog/monolog suggests installing rollbar/rollbar (Allow sending log messages to Rollbar) monolog/monolog suggests installing ruflin/elastica (Allow sending log messages to an Elastic Search server) league/flysystem suggests installing league/flysystem-aws-s3-v2 (Allows you to use S3 storage with AWS SDK v2) league/flysystem suggests installing league/flysystem-aws-s3-v3 (Allows you to use S3 storage with AWS SDK v3) league/flysystem suggests installing league/flysystem-azure (Allows you to use Windows Azure Blob storage) league/flysystem suggests installing league/flysystem-cached-adapter (Flysystem adapter decorator for metadata caching) league/flysystem suggests installing league/flysystem-eventable-filesystem (Allows you to use EventableFilesystem) league/flysystem suggests installing league/flysystem-rackspace (Allows you to use Rackspace Cloud Files) league/flysystem suggests installing league/flysystem-sftp (Allows you to use SFTP server storage via phpseclib) league/flysystem suggests installing league/flysystem-webdav (Allows you to use WebDAV storage) league/flysystem suggests installing league/flysystem-ziparchive (Allows you to use ZipArchive adapter) league/flysystem suggests installing spatie/flysystem-dropbox (Allows you to use Dropbox storage) league/flysystem suggests installing srmklive/flysystem-dropbox-v2 (Allows you to use Dropbox storage for PHP 5 applications) laravel/framework suggests installing aws/aws-sdk-php (Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.0).) laravel/framework suggests installing ext-memcached (Required to use the memcache cache driver.) laravel/framework suggests installing ext-posix (Required to use all features of the queue worker.) laravel/framework suggests installing ext-redis (Required to use the Redis cache and queue drivers (^4.0|^5.0).) laravel/framework suggests installing league/flysystem-aws-s3-v3 (Required to use the Flysystem S3 driver (^1.0).) laravel/framework suggests installing league/flysystem-cached-adapter (Required to use the Flysystem cache (^1.0).) laravel/framework suggests installing league/flysystem-sftp (Required to use the Flysystem SFTP driver (^1.0).) laravel/framework suggests installing nyholm/psr7 (Required to use PSR-7 bridging features (^1.2).) laravel/framework suggests installing pda/pheanstalk (Required to use the beanstalk queue driver (^4.0).) laravel/framework suggests installing pusher/pusher-php-server (Required to use the Pusher broadcast driver (^4.0).) laravel/framework suggests installing wildbit/swiftmailer-postmark (Required to use Postmark mail driver (^3.0).) guzzlehttp/psr7 suggests installing laminas/laminas-httphandlerrunner (Emit PSR-7 responses) psy/psysh suggests installing ext-pdo-sqlite (The doc command requires SQLite to work.) psy/psysh suggests installing ext-posix (If you have PCNTL, you'll want the POSIX extension as well.) psy/psysh suggests installing hoa/console (A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit.) predis/predis suggests installing ext-phpiredis (Allows faster serialization and deserialization of the Redis protocol) doctrine/cache suggests installing alcaeus/mongo-php-adapter (Required to use legacy MongoDB driver) thecodingmachine/graphqlite suggests installing beberlei/porpaginas (If you want automatic pagination in your GraphQL types) symfony/psr-http-message-bridge suggests installing nyholm/psr7 (For a super lightweight PSR-7/17 implementation) barryvdh/reflection-docblock suggests installing dflydev/markdown (~1.0) barryvdh/reflection-docblock suggests installing erusev/parsedown (~1.0) filp/whoops suggests installing whoops/soap (Formats errors as SOAP responses) facade/ignition suggests installing laravel/telescope (^3.1) nunomaduro/larastan suggests installing orchestra/testbench (^4.0 || ^5.0) symfony/dependency-injection suggests installing symfony/proxy-manager-bridge (Generate service proxies to lazy load them) symfony/dependency-injection suggests installing symfony/yaml symfony/config suggests installing symfony/yaml (To use the yaml reference dumper) sebastian/global-state suggests installing ext-uopz (*) sebastian/environment suggests installing ext-posix (*) phpunit/php-code-coverage suggests installing ext-pcov (*) Generating optimized autoload files ocramius/package-versions: Generating version class... ocramius/package-versions: ...done generating version class > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: barryvdh/laravel-ide-helper Discovered Package: facade/ignition Discovered Package: fideloper/proxy Discovered Package: fruitcake/laravel-cors Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Discovered Package: thecodingmachine/graphqlite-laravel Package manifest generated successfully. 100 packages you are using are looking for funding. Use the `composer fund` command to find out more! phpstan/extension-installer: Extensions installed > ergebnis/phpstan-rules: installed > nesbot/carbon: installed > nunomaduro/larastan: installed > phpstan/phpstan-deprecation-rules: installed > phpstan/phpstan-phpunit: installed > phpstan/phpstan-strict-rules: installed > [ $COMPOSER_DEV_MODE -eq 1 ] && vendor/bin/phpcs --config-set installed_paths ./../../../dev/phpcs/ && vendor/bin/phpcs --config-set default_standard Oneserv || echo 0 Using config file: /mnt/c/Users/heuser/PhpstormProjects/ofml/vendor/squizlabs/php_codesniffer/CodeSniffer.conf Config value "installed_paths" added successfully Using config file: /mnt/c/Users/heuser/PhpstormProjects/ofml/vendor/squizlabs/php_codesniffer/CodeSniffer.conf Config value "default_standard" added successfully

I also tried to it without any other package, that means I uninstalled all packages listed in the dev section of the composer.json with the exception of phpunit, phpstan, phpstan-phpunit and extension-installer. I still got the same errors.

I'm absolutely clueless what could trigger these errors.

ondrejmirtes commented 3 years ago

Hi, this can mean only one thing - SoapHelper is a final class :) And they can't be mocked.

t-heuser commented 3 years ago

Ah okay. Yeah it's final and I bypass this with "dg/bypass-finals" to be able to mock the classes. This is the result of me also using ergebnis/phpstan-rules which has the rule that every non-anonymous has to be final (ergebnis/phpstan-rules). Do you have an idea how to bypass this without removing ergebnis/phpstan.rules? Should I just add an regex which excludes these errors?

ondrejmirtes commented 3 years ago

bypass-finals used to work without a problem but right now there's an incompatibility that's hard to solve: https://github.com/phpstan/phpstan/issues/3854 feel free to subscribe to that issue, thanks.

t-heuser commented 3 years ago

Okay, thanks a lot for your help.

github-actions[bot] commented 3 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.