espend.de
PHPUnit Enhancement

Additional feature for better PHPUnit support

Links

GitHub

Issues & Source Code

JetBrains Marketplace

Features

Work in progress

Feature list

  • Method autocompletion for class, abstract class and trait mock objects;
    • Type providers: getMock, getMockForAbstractClass, etc. will return mock object with methods of mocking class and PHPUnit_Framework_MockObject_MockObject;
    • Supported PHPUnit methods:
      • PHPUnit_Framework_MockObject_MockBuilder::setMethods
      • PHPUnit_Framework_TestCase::getMock
      • PHPUnit_Framework_TestCase::getMockClass
      • PHPUnit_Framework_TestCase::getMockForAbstractClass
      • PHPUnit_Framework_TestCase::getMockForTrait
      • PHPUnit_Framework_MockObject_Builder_InvocationMocker::method
  • Code navigation (go to declaration, find usages, etc.) and refactoring (rename methods);
  • Highlighting of incorrect method usages;
  • Prophecy support.
  • Mockery support.

Mocks

/** @var $x \PHPUnit\Framework\TestCase */
$x->createMock(Foo::class)->bar();
/** @var $x \PHPUnit\Framework\TestCase */
$x->prophesize(Foo::class)->bar();
class Foo extends \PHPUnit\Framework\TestCase
{
   public function foobar()
   {
       $foo = $this->createMock(Foo::class);
       $foo->method('<caret>')
   }
}
class Foo extends \PHPUnit\Framework\TestCase
{
   public function testFoo()
   {
       $bar = $this->createPartialMock(\\Foo\Bar::class, ['<caret>']);
   }
}
class Foo extends \PHPUnit\Framework\TestCase
{
   public function setUp()
   {
       $this->foo = $this->createMock('Foo\Bar');
   }
   public function foobar()
   {
       $this->foo->method('<caret>');
   }
}
class FooTest extends \PHPUnit\Framework\TestCase
    {
        public function setUp()
        {
            $this->foo = $this->prophesize(Foo::class);
        }
        public function testFoobar()
        {
            $this->foo->getBar()->willReturn();
        }
    }
class FooTest extends \PHPUnit\Framework\TestCase
    {
        public function setUp()
        {
            $this->foo = $this->getMockBuilder(\Foo::class);
        }
        public function testFoobar()
        {
            $this->foo->getMock()->bar();
        }
    }

Prophecy

class FooTest extends \PHPUnit\Framework\TestCase
{
    public function testFoobar()
    {
        $foo = $this->prophesize(Foo::class);
        $foo->getBar()->willReturn();
    }
}
class FooTest extends \PHPUnit\Framework\TestCase
{
    public function setUp()
    {
        $this->foo = $this->prophesize(Foo::class);
    }
    
    public function testFoobar()
    {
        $this->foo->getBar()->willReturn();
    }
}

class FooTest extends \PHPUnit\Framework\TestCase
    {
        public function testFoobar()
        {
            $foo = $this->prophesize(Foo::class);
            $foo->reveal()->getBar();
        }
    }

Intention / Generator

Use intention / generator to add new method mocks. Every caret position inside a mock object is detected

$foo = $this->getMockBuilder(Foobar::class)->getMock();
$foo->method('getFoobar')->willReturn();

$foo = $this->createMock(Foobar::class);
$foo->method('getFoobar')->willReturn();
$this->foobar = $this->getMockBuilder(Foobar::class)->getMock();
// ...
$this->foobar->method('getFoobar')->willReturn();

$this->foobar = $this->createMock(Foobar::class);
// ...
$this->foobar->method('getFoobar')->willReturn();
new Foobar();
// ...
new Foobar(
    $this->createMock(Foo::class),
    $this->createMock(FooBar::class)
);
/**
 * @expectedException \Foo\FooException
 */
public function testExpectedException()
{
    $foo = new FooBar();
    $foo->throwFooException();
}

Examples

PHPUnit_Framework_MockObject_MockBuilder::setMethods

PHPUnit_Framework_MockObject_Builder_InvocationMocker::method

PHPUnit_Framework_MockObject_Builder_InvocationMocker::method

PHPUnit Runner LineMarker

PHPUnit Prophecy

PHPUnitExpected exception

Mockery Support

Mockery Support

Mockery

Has support for

  • Method referencing and autocomplete for method string in allows, expects, shouldReceive, shouldNotReceive, shouldHaveReceived, shouldNotHaveReceived. As well as in Generated partial mocks.
  • Highlighting for incorrect methods used inside an allows etc., when method is private, protected, or not found.
  • Type providers to enable new Mockery syntax: $mock->allows()->foo('arg')->andReturns('mocked_result').
  • Configurable inspection for replacing legacy Mockery syntax: replacing$mock->shouldReceive("foo")->with("arg")->andReturn("result") with $mock->allows()->foo("arg")->andReturns("result").

Referencing

In the following code snippets referencing, autocompletion, and refactoring are supported at the carets. Note that these all work with aliases, overloaded mocks, proxies, and partial mocks.

class Foo extends Mockery\Adapter\Phpunit\MockeryTestCase
{
    protected function setUp(): void
    {
        $mock = Mockery::mock('MockeryPlugin\DemoProject\Dependency');
        $mock->allows('fo<caret>o');
        $mock->expects('f<caret>oo');
        $mock->shouldReceive('ba<caret>r');
        $mock->shouldNotReceive('b<caret>ar');
    }
}
class Foo extends Mockery\Adapter\Phpunit\MockeryTestCase
{
    protected function setUp(): void
    {
        $this->mock = Mockery::mock('MockeryPlugin\DemoProject\Dependency');
    }
    
    public function test(): void
    {
        $this->mock->allows('fo<caret>o')->andReturns('result');
    }
}
class Foo extends Mockery\Adapter\Phpunit\MockeryTestCase
{
    protected function setUp(): void
    {
        $mock = Mockery::spy('MockeryPlugin\DemoProject\Dependency');
        // ...
        $mock->shouldHaveReceived('b<caret>ar');
        $mock->shouldNotHaveReceived('fo<caret>o');
    }
}
class Foo extends Mockery\Adapter\Phpunit\MockeryTestCase
{
    protected function setUp(): void
    {
        $mock = Mockery::mock('MockeryPlugin\DemoProject\Dependency');
        $mock->shouldReceive('foo', 'b<caret>ar')
        $mock->shouldReceive([
            'foo' => 'mocked result',
            'ba<caret>r' => 'mocked result'
        ]);
    }
}

Generated Partial Mocks

Method name referencing/refactoring is supported when creating generated partial mocks.

class Foo extends Mockery\Adapter\Phpunit\MockeryTestCase
{
    protected function setUp(): void
    {
        $mock = Mockery::mock(Dependency::class . "[f<caret>oo]");
        $mock = Mockery::mock('MockeryPlugin\DemoProject\Dependency[f<caret>oo]');
    }
}

Method Annotations

A warning highlight is given when the method being used is protected, private, or not found.

class Foo extends Mockery\Adapter\Phpunit\MockeryTestCase
{
    protected function setUp(): void
    {
        $mock = Mockery::mock(Dependency::class);
        $mock->expects('protectedMethod');
        $mock->expects('privateMethod');
        $mock->expects('unknownMethod');
    }
}

Inspection

An inspection is provided which will highlight legacy mockery syntax and provides a quick fix to update. Legacy Mockery uses shouldReceive/shouldNotReceive, and it gets replaced by allows/expects, e.g.

class Foo extends Mockery\Adapter\Phpunit\MockeryTestCase
{
    protected function setUp(): void
    {
        $mock = Mockery::mock(Dependency::class);
        $mock->shouldReceive('foo')->with('arg')->andReturn('result');
        // replaced by
        $mock->allows('foo')->with('arg')->andReturns('result');
        
        $mock->shouldReceive('foo')->with('arg')->andReturn('result')->once();
        // replaced by
        $mock->expects('foo')->with('arg')->andReturns('result');
    }
}

If a shouldReceive has multiple method parameters then these will get combined into an array parameter. But the inspection can be configured to prefer writing multiple allows/expects statements.

class Foo extends Mockery\Adapter\Phpunit\MockeryTestCase
{
    protected function setUp(): void
    {
        $mock = Mockery::mock(Dependency::class);
        $mock->$this->dependency->shouldReceive('foo', 'bar');
        // replaced by
        $mock->allows(['foo', 'bar']);
        
        $mock->shouldReceive('foo', 'bar')->andReturns('mocked result');
        // replaced by
        $mock->allows(['foo' => 'mocked result', 'bar' => 'mocked result']);
    }
}
class Foo extends Mockery\Adapter\Phpunit\MockeryTestCase
{
    protected function setUp(): void
    {
        $mock = Mockery::mock(Dependency::class);
        $mock->$this->dependency->shouldReceive('foo', 'bar');
        // replaced by
        $this->dependency->allows('foo');
        $this->dependency->allows('bar');
        
        $mock->shouldReceive('foo', 'bar')->andReturns('mocked result');
        // replaced by
        $this->dependency->allows('foo')->andReturns('mocked result');
        $this->dependency->allows('bar')->andReturns('mocked result');    }
}

The inspection can also be configured to prefer the new Mockery syntax in which the mocked methods are called like normal rather than as a string.

class Foo extends Mockery\Adapter\Phpunit\MockeryTestCase
{
    protected function setUp(): void
    {
        $mock = Mockery::mock(Dependency::class);
        $mock->shouldReceive('foo')->with('arg')->andReturn('result');
        // replaced by
        $mock->allows()->foo('arg')->andReturns('result');
        
        $mock->shouldReceive('foo')->with('arg')->andReturn('result')->once();
        // replaced by
        $mock->expects()->foo('arg')->andReturns('result');
    }
}

New Mockery Syntax Type Provider

Type providers are implemented so that when calling allows() on a mock it will have the type of the mocked class. Further allows()->foo() will be given the type Mockery/Expectation so that methods like andReturns(..) work as expected. This extends also to expects(), shouldReceive(), shouldNotReceive() and shouldHaveReceived(). Note: this new syntax does not extend tp shouldNotHaveReceived.

In the following example the first caret has type Dependency, and the second type Expectation.

$mock = Mockery::mock(Dependency::class);
$mock->allows<caret>()->foo<caret>('arg')->andReturns('result');