-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#145: Fix bug with empty arguments #170
Changes from 6 commits
3b159c5
7c4f4a3
069c0ef
f08e3d6
fc57d8b
604756e
106ee3f
7e66c4a
4b2fcae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2,12 +2,13 @@ | |||||||||
|
||||||||||
namespace RonasIT\Support\Traits; | ||||||||||
|
||||||||||
use Exception; | ||||||||||
use Illuminate\Support\Arr; | ||||||||||
use Closure; | ||||||||||
use Illuminate\Support\Arr; | ||||||||||
use phpmock\phpunit\PHPMock; | ||||||||||
use PHPUnit\Framework\MockObject\MockObject; | ||||||||||
use PHPUnit\Framework\MockObject\Rule\InvokedCount; | ||||||||||
use ReflectionFunction; | ||||||||||
use ReflectionMethod; | ||||||||||
|
||||||||||
trait MockTrait | ||||||||||
{ | ||||||||||
|
@@ -129,30 +130,63 @@ protected function assertArguments( | |||||||||
string $class, | ||||||||||
string $function, | ||||||||||
int $callIndex, | ||||||||||
bool $isClass = true | ||||||||||
bool $isClass = true, | ||||||||||
): void { | ||||||||||
$reflection = $isClass | ||||||||||
? new ReflectionMethod($class, $function) | ||||||||||
: new ReflectionFunction($function); | ||||||||||
|
||||||||||
$expectedCount = count($expected); | ||||||||||
$actualCount = count($actual); | ||||||||||
$parameters = $reflection->getParameters(); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
$requiredParametersCount = count(array_filter($parameters, fn ($param) => !$param->isOptional())); | ||||||||||
|
||||||||||
$this->assertArgumentCount($expectedCount, $actualCount, $requiredParametersCount, $function); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's move counting into the assertArgumentCount function
Suggested change
|
||||||||||
|
||||||||||
$this->fillOptionalArguments($parameters, $actual, $expected, $isClass); | ||||||||||
|
||||||||||
$message = $isClass | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
? "Class '{$class}'\nMethod: '{$function}'\nMethod call index: {$callIndex}" | ||||||||||
: "Namespace '{$class}'\nFunction: '{$function}'\nCall index: {$callIndex}"; | ||||||||||
|
||||||||||
$this->compareArguments($actual, $expected, $message); | ||||||||||
} | ||||||||||
|
||||||||||
protected function assertArgumentCount(int $expectedCount, int $actualCount, int $requiredParametersCount, string $function): void | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
{ | ||||||||||
if ($expectedCount !== $actualCount) { | ||||||||||
$requiredParametersCount = count(array_filter($actual, fn ($item) => $item !== self::OPTIONAL_ARGUMENT_NAME)); | ||||||||||
$this->assertFalse( | ||||||||||
$expectedCount < $requiredParametersCount, | ||||||||||
"Failed assert that function {$function} was called with {$expectedCount} require arguments, actually it calls with {$requiredParametersCount} require arguments." | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
); | ||||||||||
|
||||||||||
if ($expectedCount > $actualCount || $expectedCount < $requiredParametersCount) { | ||||||||||
throw new Exception("Failed assert that function {$function} was called with {$expectedCount} arguments, actually it calls with {$actualCount} arguments."); | ||||||||||
} | ||||||||||
$this->assertFalse( | ||||||||||
$expectedCount > $actualCount, | ||||||||||
"Failed assert that function {$function} was called with {$expectedCount} arguments, actually it calls with {$actualCount} arguments." | ||||||||||
); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
$expected = array_pad($expected, $actualCount, self::OPTIONAL_ARGUMENT_NAME); | ||||||||||
protected function fillOptionalArguments(array $parameters, array &$actual, array &$expected, bool $isClass): void | ||||||||||
{ | ||||||||||
foreach ($parameters as $index => $parameter) { | ||||||||||
if (!isset($expected[$index]) && $parameter->isOptional()) { | ||||||||||
$expected[$index] = $parameter->getDefaultValue(); | ||||||||||
} | ||||||||||
|
||||||||||
$message = ($isClass) | ||||||||||
? "Class '{$class}'\nMethod: '{$function}'\nMethod call index: {$callIndex}" | ||||||||||
: "Namespace '{$class}'\nFunction: '{$function}'\nCall index: {$callIndex}"; | ||||||||||
if (!$isClass && $actual[$index] === 'optionalParameter') { | ||||||||||
$actual[$index] = $expected[$index] ?? $parameter->getDefaultValue(); | ||||||||||
DenTray marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please move actual args filling before the expected args filling
Suggested change
|
||||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
protected function compareArguments(array $actual, array $expected, string $message): void | ||||||||||
{ | ||||||||||
foreach ($actual as $index => $argument) { | ||||||||||
$this->assertEquals( | ||||||||||
$expected[$index], | ||||||||||
$argument, | ||||||||||
"Failed asserting that arguments are equals to expected.\n{$message}\nArgument index: {$index}" | ||||||||||
"Failed asserting that arguments are equal to expected.\n{$message}\nArgument index: {$index}" | ||||||||||
); | ||||||||||
} | ||||||||||
} | ||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2,8 +2,9 @@ | |||||||||||||||||
|
||||||||||||||||||
namespace RonasIT\Support\Tests; | ||||||||||||||||||
|
||||||||||||||||||
use PHPUnit\Framework\ExpectationFailedException; | ||||||||||||||||||
use RonasIT\Support\Tests\Support\Mock\TestMockClass; | ||||||||||||||||||
use RonasIT\Support\Traits\MockTrait; | ||||||||||||||||||
use Exception; | ||||||||||||||||||
|
||||||||||||||||||
class MockTraitTest extends HelpersTestCase | ||||||||||||||||||
{ | ||||||||||||||||||
|
@@ -46,26 +47,101 @@ public function testMockWithDifferentFunction() | |||||||||||||||||
$this->functionCall('rand', [1, 5], 2), | ||||||||||||||||||
$this->functionCall('is_array', [123]), | ||||||||||||||||||
$this->functionCall('rand', [6, 10], 7), | ||||||||||||||||||
$this->functionCall('uniqid', [], '0987654321'), | ||||||||||||||||||
$this->functionCall('uniqid', ['prefix'], '0987654321'), | ||||||||||||||||||
$this->functionCall(name: 'uniqid', result: '0987654321'), | ||||||||||||||||||
$this->functionCall('array_slice', [[1, 2, 3, 4, 5], 2, 2], [3, 4]), | ||||||||||||||||||
$this->functionCall('array_slice', [[1, 2, 3, 4, 5], 2], [3, 4, 5]), | ||||||||||||||||||
$this->functionCall('array_slice', [[1, 2, 3, 4, 5], 2, 2, 'preserve_keys'], [3, 4]), | ||||||||||||||||||
]); | ||||||||||||||||||
|
||||||||||||||||||
$this->assertEquals(2, rand(1, 5)); | ||||||||||||||||||
$this->assertTrue(is_array(123)); | ||||||||||||||||||
$this->assertEquals(7, rand(6, 10)); | ||||||||||||||||||
$this->assertEquals('0987654321', uniqid()); | ||||||||||||||||||
$this->assertEquals('0987654321', uniqid()); | ||||||||||||||||||
$this->assertEquals([3, 4], array_slice([1, 2, 3, 4, 5], 2, 2)); | ||||||||||||||||||
$this->assertEquals([3, 4, 5], array_slice([1, 2, 3, 4, 5], 2)); | ||||||||||||||||||
$this->assertEquals([3, 4], array_slice([1, 2, 3, 4, 5], 2, 2)); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
public function testMockNativeFunctionWhenLessRequiredParameters() | ||||||||||||||||||
{ | ||||||||||||||||||
$this->expectException(ExpectationFailedException::class); | ||||||||||||||||||
$this->expectExceptionMessage('Failed assert that function array_slice was called with 1 require arguments, actually it calls with 2 require arguments.'); | ||||||||||||||||||
|
||||||||||||||||||
$this->mockNativeFunction('RonasIT\Support\Tests', [ | ||||||||||||||||||
$this->functionCall('array_slice', [[1, 2, 3, 4, 5]], [3, 4]), | ||||||||||||||||||
]); | ||||||||||||||||||
|
||||||||||||||||||
array_slice([1, 2, 3, 4, 5], 2, 2); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
public function testMockNativeFunctionWhenMoreExpectedParameters() | ||||||||||||||||||
{ | ||||||||||||||||||
$this->expectException(ExpectationFailedException::class); | ||||||||||||||||||
$this->expectExceptionMessage('Failed assert that function array_slice was called with 5 arguments, actually it calls with 4 arguments.'); | ||||||||||||||||||
|
||||||||||||||||||
$this->mockNativeFunction('RonasIT\Support\Tests', [ | ||||||||||||||||||
$this->functionCall('array_slice', [[1, 2, 3, 4, 5], 2, 2, 'preserve_keys', 'extra_parameter'], [3, 4]), | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's use multiline call style with named args |
||||||||||||||||||
]); | ||||||||||||||||||
|
||||||||||||||||||
array_slice([1, 2, 3, 4, 5], 2, 2, 'preserve_keys'); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
public function testMockNativeFunctionWhenDifferentResult() | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
{ | ||||||||||||||||||
$this->mockNativeFunction('RonasIT\Support\Tests', [ | ||||||||||||||||||
$this->functionCall('array_slice', [[1, 2, 3, 4, 5], 2, 2], [3, 4]), | ||||||||||||||||||
]); | ||||||||||||||||||
|
||||||||||||||||||
$this->assertNotEquals([3, 4, 5], array_slice([1, 2, 3, 4, 5], 2, 2)); | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
public function testMockFunctionInClass() | ||||||||||||||||||
{ | ||||||||||||||||||
$mock = $this->mockClass(TestMockClass::class, [ | ||||||||||||||||||
$this->functionCall('mockFunction', ['firstRequired', 'secondRequired'], 'mockFunction'), | ||||||||||||||||||
$this->functionCall('mockFunction', ['firstRequired', 'secondRequired', 'firstOptional'], 'mockFunction'), | ||||||||||||||||||
$this->functionCall('mockFunction', ['firstRequired', 'secondRequired', 'firstOptional', 'secondOptional'], 'mockFunction'), | ||||||||||||||||||
]); | ||||||||||||||||||
|
||||||||||||||||||
$this->assertEquals('mockFunction', ($mock->mockFunction('firstRequired', 'secondRequired'))); | ||||||||||||||||||
$this->assertEquals('mockFunction', ($mock->mockFunction('firstRequired', 'secondRequired', 'firstOptional'))); | ||||||||||||||||||
$this->assertEquals('mockFunction', ($mock->mockFunction('firstRequired', 'secondRequired', 'firstOptional', 'secondOptional'))); | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
public function testMockFunctionInClassWhenDifferentResult() | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
{ | ||||||||||||||||||
$mock = $this->mockClass(TestMockClass::class, [ | ||||||||||||||||||
$this->functionCall('mockFunction', ['firstRequired', 'secondRequired'], 'mockFunction'), | ||||||||||||||||||
]); | ||||||||||||||||||
|
||||||||||||||||||
$this->assertNotEquals('result', $mock->mockFunction('firstRequired', 'secondRequired')); | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
public function testMockFunctionInClassWhenLessRequiredParameters() | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
{ | ||||||||||||||||||
$this->expectException(ExpectationFailedException::class); | ||||||||||||||||||
$this->expectExceptionMessage('Failed assert that function mockFunction was called with 1 require arguments, actually it calls with 2 require arguments.'); | ||||||||||||||||||
|
||||||||||||||||||
$this->assertArguments( | ||||||||||||||||||
actual: ['firstRequired', 'secondRequired', 'string', null], | ||||||||||||||||||
expected: ['firstRequired'], | ||||||||||||||||||
class: TestMockClass::class, | ||||||||||||||||||
function: 'mockFunction', | ||||||||||||||||||
callIndex: 0, | ||||||||||||||||||
); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
public function testAssertArgumentMismatchBetweenExpectedAndActualArguments() | ||||||||||||||||||
public function testMockFunctionInClassWhenMoreExpectedParameters() | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
{ | ||||||||||||||||||
$this->expectException(Exception::class); | ||||||||||||||||||
$this->expectExceptionMessage('Failed assert that function testFunction was called with 2 arguments, actually it calls with 1 arguments.'); | ||||||||||||||||||
$this->expectException(ExpectationFailedException::class); | ||||||||||||||||||
$this->expectExceptionMessage('Failed assert that function mockFunction was called with 5 arguments, actually it calls with 4 arguments.'); | ||||||||||||||||||
|
||||||||||||||||||
$this->assertArguments(['test'], ['test', ''], 'TestClass', 'testFunction', 0); | ||||||||||||||||||
$this->assertArguments( | ||||||||||||||||||
actual: ['firstRequired', 'secondRequired', 'string', null], | ||||||||||||||||||
expected: ['firstRequired', 'secondRequired', 'firstOptional', 'secondOptional', 'thirdOptional'], | ||||||||||||||||||
class: TestMockClass::class, | ||||||||||||||||||
function: 'mockFunction', | ||||||||||||||||||
callIndex: 0, | ||||||||||||||||||
); | ||||||||||||||||||
} | ||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
|
||
namespace RonasIT\Support\Tests\Support\Mock; | ||
|
||
class TestMockClass | ||
{ | ||
public function mockFunction( | ||
string $firsrRequeredParam, | ||
string $secondRequiredParam, | ||
string $firstOptionalParam = 'string', | ||
?string $secondOptionalParam = null, | ||
): string { | ||
return 'mockFunction'; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.