Giter Site home page Giter Site logo

phpstan-mockery's Introduction

PHPStan - PHP Static Analysis Tool

PHPStan

Build Status Latest Stable Version Total Downloads License PHPStan Enabled


PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs even before you write tests for the code. It moves PHP closer to compiled languages in the sense that the correctness of each line of the code can be checked before you run the actual line.

Read more about PHPStan »

Try out PHPStan on the on-line playground! »

Sponsors

TheCodingMachine     Private Packagist
CDN77     Blackfire.io
iO     Amezmo
ShipMonk     Togetter
RightCapital     ContentKing
ZOL     Psyonix
Shopware     Craft CMS
Worksome     campoint AG
Crisp.nl     Inviqa
GetResponse     EdgeNext
Fame Helsinki

You can now sponsor my open-source work on PHPStan through GitHub Sponsors.

Does GitHub already have your 💳? Do you use PHPStan to find 🐛 before they reach production? Send a couple of 💸 a month my way too. Thank you!

One-time donations through PayPal are also accepted. To request an invoice, contact me through e-mail.

Documentation

All the documentation lives on the phpstan.org website:

PHPStan Pro

PHPStan Pro is a paid add-on on top of open-source PHPStan Static Analysis Tool with these premium features:

  • Web UI for browsing found errors, you can click and open your editor of choice on the offending line.
  • Continuous analysis (watch mode): scans changed files in the background, refreshes the UI automatically.

Try it on PHPStan 0.12.45 or later by running it with the --pro option. You can create an account either by following the on-screen instructions, or by visiting account.phpstan.com.

After 30-day free trial period it costs 7 EUR for individuals monthly, 70 EUR for teams (up to 25 members). By paying for PHPStan Pro, you're supporting the development of open-source PHPStan.

You can read more about it on PHPStan's website.

Code of Conduct

This project adheres to a Contributor Code of Conduct. By participating in this project and its community, you are expected to uphold this code.

Contributing

Any contributions are welcome. PHPStan's source code open to pull requests lives at phpstan/phpstan-src.

phpstan-mockery's People

Contributors

adaamz avatar bbatsche avatar dependabot[bot] avatar fancyweb avatar herndlm avatar kocal avatar localheinz avatar lookyman avatar martinssipenko avatar michaeldemeyer avatar msvrtan avatar ondrejmirtes avatar renovate-bot avatar renovate[bot] avatar ruudk avatar simpod avatar staabm avatar tomasvotruba avatar villfa avatar wimski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

phpstan-mockery's Issues

Support makePartial()

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..

Missing mock type information for Mockery\Expectation::getMock()

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>.

Some currently unsupported cases

Some expectation methods

$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().

Partial mocks

$mock = Mockery::mock('Foo\Bar[method1,method2]');

Currently, $mock is assumed to be of type Foo\Bar[method1,method2]

Methods returning Mock

$mock = Mockery::mock(Bar::class)->shouldDeferMissing();

Currently, methods returning Mockery\Mock will just return that, instead of an intersection type.

Passing an array as $methodNames argument in LegacyMockerInterface::shouldReceive() results in error

Setup

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

Issue

No errors

Using the most basic shouldReceive syntax produces no PHPStan errors.

/** @var MyClass|MockInterface */
$myMock = Mockery::mock(MyClass::class);
$myMock
    ->shouldReceive('someMethod')
    ->andReturn('resultString');

PHPStan error

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

Call to an undefined method xxx::shouldReceive()

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'));
    }
}

Infer mock type when defined in setUp

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.

Calling PHPStan\Mockery\Type\Expects::with() is not covered by backward compatibility promise.

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?

New release for phpstan 0.12?

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?

Require unit tests to call `Mockery::close()` or use `MockeryPHPUnitIntegration` trait

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.

Incorrect doc blocks for shouldHaveReceived and shouldNotHaveReceived

The doc blocks for the shouldHaveReceived and shouldNotHaveReceived methods in

public function shouldHaveReceived($method, $args = null);
are incorrect.

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.

Call to protected method xxx()

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

Expects Mockery\Expectation, PHPStan\Mockery\Type\Expects given.

$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

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

composer
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-actions
.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

  • Check this box to trigger a request for Renovate to run again on this repository

PHPStan reports error when using alternative syntax of shouldReceive method

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?

Call to method PHPStan\Mockery\Type\Expects::never() on a separate line has no effect.

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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.