Skip to content

Commit

Permalink
Add handling for more complex declaration syntax:
Browse files Browse the repository at this point in the history
 - alias:
 - overload:
 - Multiple interface names
 - Constructor arguments
 - Method declaration
  • Loading branch information
bbatsche authored and ondrejmirtes committed Jul 19, 2019
1 parent 0057dac commit a013b23
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 9 deletions.
35 changes: 28 additions & 7 deletions src/Mockery/Type/MockDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,36 @@ public function getTypeFromStaticMethodCall(
return $defaultReturnType;
}

$classType = $scope->getType($methodCall->args[0]->value);
if (!$classType instanceof ConstantStringType) {
return $defaultReturnType;
$types = [$defaultReturnType];
foreach ($methodCall->args as $arg) {
$classType = $scope->getType($arg->value);
if (!$classType instanceof ConstantStringType) {
continue;
}

$value = $classType->getValue();
if (substr($value, 0, 6) === 'alias:') {
$value = substr($value, 6);
}
if (substr($value, 0, 9) === 'overload:') {
$value = substr($value, 9);
}
if (substr($value, -1) === ']' && strpos($value, '[') !== false) {
$value = substr($value, 0, strpos($value, '['));
}

if (strpos($value, ',') !== false) {
$interfaceNames = explode(',', str_replace(' ', '', $value));
} else {
$interfaceNames = [$value];
}

foreach ($interfaceNames as $name) {
$types[] = new ObjectType($name);
}
}

return TypeCombinator::intersect(
$defaultReturnType,
new ObjectType($classType->getValue())
);
return TypeCombinator::intersect(...$types);
}

}
10 changes: 10 additions & 0 deletions tests/Mockery/Baz.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php declare(strict_types = 1);

namespace PHPStan\Mockery;

interface Baz
{

public function doFoo(): ?string;

}
10 changes: 10 additions & 0 deletions tests/Mockery/Buzz.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php declare(strict_types = 1);

namespace PHPStan\Mockery;

interface Buzz
{

public function doBuzz(): ?string;

}
12 changes: 10 additions & 2 deletions tests/Mockery/Foo.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@

namespace PHPStan\Mockery;

class Foo
class Foo implements Baz
{

/** @var bool */
private $optional;

public function __construct(bool $optional = true)
{
$this->optional = $optional;
}

public function doFoo(): ?string
{
if (rand(0, 1) === 0) {
if ($this->optional) {
return null;
}

Expand Down
33 changes: 33 additions & 0 deletions tests/Mockery/IsolatedMockeryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php declare(strict_types = 1);

namespace PHPStan\Mockery;

/**
* @runTestsInSeparateProcesses
*/
class IsolatedMockeryTest extends \PHPUnit\Framework\TestCase
{

public function testAliasMock(): void
{
$fooMock = \Mockery::mock('alias:' . Foo::class);
$this->requireFoo($fooMock);

$fooMock->shouldReceive('doFoo')->andReturn('bar');
self::assertSame('bar', $fooMock->doFoo());
}

public function testOverloadMock(): void
{
$fooMock = \Mockery::mock('overload:' . Foo::class);
$this->requireFoo($fooMock);

$fooMock->shouldReceive('doFoo')->andReturn('bar');
self::assertSame('bar', $fooMock->doFoo());
}

private function requireFoo(Foo $foo): void
{
}

}
48 changes: 48 additions & 0 deletions tests/Mockery/MockeryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,56 @@ public function testMockFromProperty(): void
self::assertSame('foo', $this->fooMock->doFoo());
}

public function testMockInterface(): void
{
$interfaceMock = \Mockery::mock(Baz::class, Buzz::class);
$this->requireBaz($interfaceMock);
$this->requireBuzz($interfaceMock);

$interfaceMock->shouldReceive('doFoo')->andReturn('bar');
self::assertSame('bar', $interfaceMock->doFoo());
}

public function testMockFooWithInterfaces(): void
{
$fooMock = \Mockery::mock(Foo::class, Baz::class . ', ' . Buzz::class);
$this->requireFoo($fooMock);
$this->requireBaz($fooMock);
$this->requireBuzz($fooMock);

$fooMock->shouldReceive('doFoo')->andReturn('bar');
self::assertSame('bar', $fooMock->doFoo());
}

public function testMockWithConstructorArgs(): void
{
$fooMock = \Mockery::mock(Foo::class, [true]);
$this->requireFoo($fooMock);

$fooMock->shouldReceive('doFoo')->andReturn('bar');
self::assertSame('bar', $fooMock->doFoo());
}

public function testMockWithInterfaceAndConstructorArgs(): void
{
$fooMock = \Mockery::mock(Foo::class, Buzz::class, [true]);
$this->requireFoo($fooMock);
$this->requireBuzz($fooMock);

$fooMock->shouldReceive('doFoo')->andReturn('bar');
self::assertSame('bar', $fooMock->doFoo());
}

private function requireFoo(Foo $foo): void
{
}

private function requireBaz(Baz $baz): void
{
}

private function requireBuzz(Buzz $buzz): void
{
}

}

0 comments on commit a013b23

Please sign in to comment.