phpstan-phpunit icon indicating copy to clipboard operation
phpstan-phpunit copied to clipboard

getMockBuilder() should also work with non-existent classes

Open Majkl578 opened this issue 7 years ago • 7 comments

TestCase::getMockBuilder() can be used with undefined classes as well. In this case, the mock system creates dummy empty class of the given name.

This is used in Doctrine on some places, i.e. for testing event listeners that are based on naming, not interface.

Without this patch, we were getting the following errors:

 ------ ---------------------------------------------------------------------------- 
  Line   tests/Doctrine/Tests/DBAL/ConnectionTest.php                                
 ------ ---------------------------------------------------------------------------- 
  144    Call to method expects() on an unknown class ConnectDispatchEventListener.  
 ------ ---------------------------------------------------------------------------- 

 ------ ------------------------------------------------------------------------------------- 
  Line   tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php      
 ------ ------------------------------------------------------------------------------------- 
  349    Call to method expects() on an unknown class ListTableColumnsDispatchEventListener.  
  377    Call to method expects() on an unknown class ListTableIndexesDispatchEventListener.  
 ------ ------------------------------------------------------------------------------------- 

 ------ ------------------------------------------------------------------------------------- 
  Line   tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php                     
 ------ ------------------------------------------------------------------------------------- 
  372    Call to method expects() on an unknown class GetCreateTableSqlDispatchEvenListener.  
  375    Call to method expects() on an unknown class GetCreateTableSqlDispatchEvenListener.  
  396    Call to method expects() on an unknown class GetDropTableSqlDispatchEventListener.   
  421    Call to method expects() on an unknown class GetAlterTableSqlDispatchEvenListener.   
  424    Call to method expects() on an unknown class GetAlterTableSqlDispatchEvenListener.   
  427    Call to method expects() on an unknown class GetAlterTableSqlDispatchEvenListener.   
  430    Call to method expects() on an unknown class GetAlterTableSqlDispatchEvenListener.   
  433    Call to method expects() on an unknown class GetAlterTableSqlDispatchEvenListener.   
 ------ -------------------------------------------------------------------------------------

No tests for Type\PHPUnit - how to add some?

Majkl578 avatar Aug 18 '18 01:08 Majkl578

@lookyman @ondrejmirtes Can you please review this as a bugfix for 0.10.x? Thanks.

Majkl578 avatar Aug 20 '18 15:08 Majkl578

Hi, what about createMock? That one needs the same fix probably: https://github.com/phpstan/phpstan-phpunit/blob/master/src/Type/PHPUnit/CreateMockDynamicReturnTypeExtension.php

ondrejmirtes avatar Aug 20 '18 15:08 ondrejmirtes

According to this comment from Sebastian Bergmann, non-existent classes cannot be used with createMock() API, but can be used with Mock Builder API:

Classes that do not exist cannot be doubled using the new createMock() API.

and

Classes that do not exist can still be doubled using the Mock Builder API.

It's 2 years old commit though, it may've changed.

Majkl578 avatar Aug 20 '18 15:08 Majkl578

Can you verify that with a quick test?

ondrejmirtes avatar Aug 20 '18 15:08 ondrejmirtes

Seems to still be valid:

<?php

class NonExistentTest extends PHPUnit\Framework\TestCase
{
    public function testCreateMock()
    {
        $mock = $this->createMock('NonExistent');
        self::assertInstanceOf(PHPUnit\Framework\MockObject\MockObject::class, $mock);
    }

    public function testGetMockBuilder()
    {
        $mock = $this->getMockBuilder('NonExistent')->getMock();
        self::assertInstanceOf(PHPUnit\Framework\MockObject\MockObject::class, $mock);
    }
}
$ vendor/bin/phpunit NonExistentTest.php
PHPUnit 7.3.1 by Sebastian Bergmann and contributors.

W.                                                                  2 / 2 (100%)

Time: 56 ms, Memory: 4.00MB

There was 1 warning:

1) NonExistentTest::testCreateMock
Cannot stub or mock class or interface "NonExistent" which does not exist

WARNINGS!
Tests: 2, Assertions: 1, Warnings: 1.

Majkl578 avatar Aug 20 '18 15:08 Majkl578

This is because it's explicitly disallowed in createMock(): https://github.com/sebastianbergmann/phpunit/blob/7.3.1/src/Framework/TestCase.php#L1311

Majkl578 avatar Aug 20 '18 15:08 Majkl578

Cool. Maybe we should modify https://github.com/phpstan/phpstan-phpunit/blob/master/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php too to return a little bit different instance of MockBuilderType after calling disallowMockingUnknownTypes that would still complain about unknown class.

ondrejmirtes avatar Aug 20 '18 15:08 ondrejmirtes