With PHPUnit, I want to test that a function is called only once in Mocked Class. I tested several cases to be sure to understand excepts() :
functionInMock is not executed (Ok, expected result : no error) :
$myMock
->expects($this->never())
->method('functionInMock')
;
functionInMock is executed 1 times (Ok, expected result : no error) :
$myMock
->expects($this->once())
->method('functionInMock')
;
functionInMock is executed 2 times (Ok, expected result : error) :
$myMock
->expects($this->once())
->method('functionInMock')
;
functionInMock is executed 1 time :
$myMock
->expects($this->exactly(2))
->method('functionInMock')
;
or
$myMock
->expects($this->exactly(999))
->method('functionInMock')
;
Why don't I have an error in this last case ? The test passes without reporting an error.
I am no sure why you have unexpected behaviour but this example works fine
<?php
// Lets name it 'SampleTest.php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
public function testSample(): void
{
$myMock = $this
->getMockBuilder(Sample::class)
->addMethods(['functionInMock'])
->getMock();
$myMock
->expects($this->exactly(2))
->method('functionInMock');
$myMock->functionInMock();
}
}
class Sample
{
public function function2InMock(): void
{
}
}
Execution
$ phpunit SampleTest.php
PHPUnit 9.1.1 by Sebastian Bergmann and contributors.
F 1 / 1 (100%)
Time: 125 ms, Memory: 6.00 MB
There was 1 failure:
1) SampleTest::testSample
Expectation failed for method name is "functionInMock" when invoked 2 time(s).
Method was expected to be called 2 times, actually called 1 times.
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
Related
I am using assertCount() method within my Symfony project and PHPUnit Functional tests. I was refactoring a method, and suddenly I does not work any more. But I am not sure what could be a problem.
I wanted to move my method that I am using multiple times from one class:
public function testPostSuccessful(): void
{
...
// rest of the request code //
$response = $this->client->getResponse();
$responseBody = json_decode($response->getContent(), true);
$this->assertArraySimilar($this->expected, $responseBody["data"][0]);
$this->assertEquals(200, $response->getStatusCode());
}
And in my other method:
protected function assertArraySimilar(array $expected, array $array)
{
// expected amount of key - value pairs are returned
$this->assertCount(6, $array);
...
// rest of the code //
}
This is throwing an error:
Failed asserting that actual size 2 matches expected size 6.
But when I dump the result like:
var_dump(count($array));die;
I get: int(6)
UPDATE: Also when I input 2 instead of 6, I got reverse error like: Failed asserting that 6 matches expected 2
What could be the problem here?
This works! Why is this working example and above one is not.
$this->assertCount(count($expected), $array);
The question is in PHP, but applies to any language using the xUnit framework.
I want a mock, that expects 140 calls to method jump.
I need to verify, that at least once there is a call with 500 as parameter.
I don't care if all the calls are 500, but I need at least one that is called with 500.
$mock = $this->getMock('Trampoline', ['jump']);
$mock->expects($this->atLeastOnce())
->method('jump')
->with($this->equalTo(500))
->will($this->returnValue(true));
$sportsman->setTramploine($mock);
$sportsman->jumpToRandomHeights($times = 140); // this calls Trampoline->jump
// I need to verify the sportsman had jumped
// to the height of 500 at least once out of the 140 jumps he is performing
In the current code, the test fails after the first invocation of jump because the first invocation had a value different of 500, meaning the atLestOnce here only indicates that the method should be called, but not that it should be called with specific value among other calls.
Solution
The missing piece of information was using callbacks inside the with. Thanks to edorian's answer below this is what worked out:
$testPassed = false;
$checkMinHeight = function ($arg) use(&$testPassed)
{
if($arg === 500)
$testPassed = true;
// return true for the mock object to consider the input valid
return true;
}
$mock = $this->getMock('Trampoline', ['jump'])
->expects($this->atLeastOnce())
->method('jump')
->with($checkMinHeight)
->will($this->returnValue(true));
$sportsman->setTramploine($mock);
$sportsman->jumpToRandomHeights($times = 1000); // this calls Trampoline->jump
// I need to verify the sportsman had jumped
// to the height of 500 at least once out of the 1000 jumps he is performing
$this->assertTrue($testPassed, "Sportsman was expected to
jump 500m at least once");
You can but the best implementation withing PHPUnits mocking API, that I could come up with, still looks quite creepy.
Another way to solve this is a little more readable way would be to create your own subclass of Trampoline and implement it there.
But for the challenge:
Assuming this class:
<?php
class FancyMocking {
function doThing($value) { }
}
and that we have $x calls and one of those has to have a $value > 200:
<?php
class FancyMockingTest extends PHPUnit_Framework_TestCase {
public function testAtLeastOfMy200CallsShouldHaveAValueGreaterThan500() {
$maxInvocations = 200;
$mock = $this->getMock('FancyMocking');
$mock->expects($this->exactly($maxInvocations))
->method('doThing')
->with($this->callback(function ($value) use ($maxInvocations) {
static $invocationCount = 0;
static $maxValue = 0;
$maxValue = max($value, $maxValue);
/* The assertion function will be called twice by PHPUnit due to implementation details, so the *2 is a hack for now */
if (++$invocationCount == $maxInvocations * 2) {
$this->assertGreaterThan(200, $maxValue, 'in 500 tries the max value didn\'t to over 200');
}
return true;
}))
->will($this->returnCallback(function ($value) {
return $value >= 200;
}));
for($i = $maxInvocations - 2; $i; --$i) {
$mock->doThing(50);
}
var_dump($mock->doThing(250));
var_dump($mock->doThing(50));
}
}
This will produce:
PHPUnit 3.7.9 by Sebastian Bergmann.
.bool(true)
bool(false)
Time: 0 seconds, Memory: 2.75Mb
OK (1 test, 2 assertions)
Meaning the call with 250 returns true an the whole test case works.
If it fails:
To make it fail we change var_dump($mock->doThing(250)); to var_dump($mock->doThing(70)); and run it again:
PHPUnit 3.7.9 by Sebastian Bergmann.
Fbool(false)
Time: 0 seconds, Memory: 2.75Mb
There was 1 failure:
1) FancyMockingTest::testAtLeastOfMy200CallsShouldHaveAValueGreaterThan500
Expectation failed for method name is equal to <string:doThing> when invoked 200 time(s)
in 500 tries the max value didn't to over 200
Failed asserting that 70 is greater than 200.
.../FancyMockingTest.php:29
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
The solution that doesn't use PHPUnits mocking API would be something like
class FancyMockingFakeImplementation extends FancyMocking, using that instead of the mock and writing the custom logic there.
What is the best way to test for multiple array keys in a PHPUnit mock with() clause?
For example, to test whether a method calls 2nd argument is an array containing a 'foo' key:
$this->stubDispatcher->expects($this->once())
->method('send')
->with('className', $this->arrayHasKey('foo'));
What I'd like to do is something like $this->arrayHasKey('foo', 'bar') while not actually matching the exact contents of the array.
You can pass assertions directly to ->with() but they are named differently.
For the list take a look at the source: https://github.com/sebastianbergmann/phpunit/blob/3.5/PHPUnit/Framework/Assert.php#L2097 and following. Everything that doesn't start with "assert" and returns a new PHPUnit_Framework_*.
While i assume #David's answer works I think this approach, using logicalAnd() is a little cleaner / easier to read.
$mock->expects($this->once())->method("myMethod")->with(
$this->logicalAnd(
$this->arrayHasKey("foo"),
$this->arrayHasKey("bar")
)
);
Working example
<?php
class MyTest extends PHPUnit_Framework_TestCase {
public function testWorks() {
$mock = $this->getMock("stdClass", array("myMethod"));
$mock->expects($this->once())->method("myMethod")->with(
$this->logicalAnd(
$this->arrayHasKey("foo"),
$this->arrayHasKey("bar")
)
);
$array = array("foo" => 1, "bar" => 2);
$mock->myMethod($array);
}
public function testFails() {
$mock = $this->getMock("stdClass", array("myMethod"));
$mock->expects($this->once())->method("myMethod")->with(
$this->logicalAnd(
$this->arrayHasKey("foo"),
$this->arrayHasKey("bar")
)
);
$array = array("foo" => 1);
$mock->myMethod($array);
}
}
Output
phpunit assertArrayKey.php
PHPUnit 3.5.13 by Sebastian Bergmann.
.F
Time: 0 seconds, Memory: 6.50Mb
There was 1 failure:
1) MyTest::testFails
Expectation failed for method name is equal to <string:myMethod> when invoked 1 time(s)
Parameter 0 for invocation stdClass::myMethod(array( <string:foo> => <integer:1> )) does not match expected value.
Failed asserting that an array has the key <string:bar>.
/home/edo/test/assertArrayKey.php:27
You can use a callback to make multiple assertions.
$this->stubDispatcher->expects($this->once())
->method('send')
->will($this->returnCallback(function($class, $array) {
self::assertEquals('className', $class);
self::assertArrayHasKey('foo', $array);
self::assertArrayHasKey('bar', $array);
}));
Edit: And if you want to make basic assertions about some parameters and more complicated assertions about the others, you can add with().
$this->stubDispatcher->expects($this->once())
->method('send')
->with('className', $this->anything())
->will($this->returnCallback(function($class, $array) {
self::assertArrayHasKey('foo', $array);
self::assertArrayHasKey('bar', $array);
}));
To be super clear, you should never pass $this->returnCallback() to with(). Same for returnValue() and throwException(). These are all directives that tell the mock what to do when the method is called. with() is for telling the mock what parameters it should accept.
Does anyone know a reliable way to distinguish between FALSE and NULL with PHPUnit?
I'm trying to distinguish from NULL and FALSE in return values in my assertions.
This fails:
$this->assertNotEquals(FALSE, NULL);
And these assertions pass:
$this->assertFalse(NULL);
$this->assertNull(FALSE);
Edit: For some context, this is to distinguish between an error state (FALSE) versus an empty result (NULL). To ensure the function is returning properly, I need to distinguish between the two.
Thanks
Edit...
As per some of the problems regarding what I am testing, I'm adding the tests.
Class testNullFalse extends PHPUnit_Framework_TestCase{
public function test_null_not_false (){
$this->assertNotEquals(FALSE, NULL, "False and null are not the same");
}
public function test_null_is_false (){
$this->assertFalse(NULL, "Null is clearly not FALSE");
}
public function test_false_is_null (){
$this->assertNull(FALSE, "False is clearly not NULL");
}
public function test_false_equals_null(){
$this->assertEquals(FALSE, NULL, "False and null are not equal");
}
public function test_false_sameas_null(){
$this->assertSame(FALSE, NULL, "False and null are not the same");
}
public function test_false_not_sameas_null(){
$this->assertNotSame(FALSE, NULL, "False and null are not the same");
}
}
And the results.
PHPUnit 3.5.10 by Sebastian Bergmann.
FFF.F.
Time: 0 seconds, Memory: 5.50Mb
There were 4 failures:
1) testNullFalse::test_null_not_false
False and null are not the same
Failed asserting that <null> is not equal to <boolean:false>.
2) testNullFalse::test_null_is_false
Null is clearly not FALSE
Failed asserting that <null> is false.
3) testNullFalse::test_false_is_null
False is clearly not NULL
Failed asserting that <boolean:false> is null.
4) testNullFalse::test_false_sameas_null
False and null are not the same
<null> does not match expected type "boolean".
FAILURES!
Tests: 6, Assertions: 6, Failures: 4.
These assertions use == which will perform type coercion. Hamcrest has identicalTo($value) which uses ===, and I believe PHPUnit has assertSame($expected, $actual) which does the same.
self::assertSame(false, $dao->getUser(-2));
Update: In answer to your comment, "It can be NULL or an object":
$user = $dao->getUser(-2);
self::assertTrue($user === null || is_object($user));
Using Hamcrest assertions is a little more expressive, especially in the event of a failure:
assertThat($dao->getUser(-2), anyOf(objectValue(), nullValue()));
Execute the comparison yourself, and use the strict type operator.
$this->assertTrue(false !== null);
http://php.net/operators.comparison
#David is right with assertSame (+1), it will do === strict comparison for you.
But let me ask you:
Which version of phpunit are you using?
This assertion:
$this->assertFalse(null);
should produce and error!
Sample Code:
<?php
class mepTest extends PHPUnit_Framework_TestCase {
public function testFalseNull() {
$this->assertFalse(null);
}
public function testNullFalse() {
$this->assertNull(false);
}
}
Results in:
phpunit mepTest.php
PHPUnit 3.5.12 by Sebastian Bergmann.
FF
Time: 0 seconds, Memory: 3.00Mb
There were 2 failures:
1) mepTest::testFalseNull
Failed asserting that <null> is false.
/home/.../mepTest.php:6
2) mepTest::testNullFalse
Failed asserting that <boolean:false> is null.
/home/.../mepTest.php:10
FAILURES!
Tests: 2, Assertions: 2, Failures: 2.
Selenium seems to be pausing for about a minute between each step (I haven't seen less than 60 seconds). Even steps that should be simple (like setSpeed) run at the same rate.
However, when I use the PHPUnit_Extensions_SeleniumTestCase class, I am able to run tests at normal speed. (Also, the slow steps run fine on a coworker's computer.)
Anyone know what I'm doing wrong? Thanks!
Here is the slow test:
debug_time(); // 0
require_once 'Testing/Selenium.php';
debug_time(); // 1
$s = new Testing_Selenium('*firefox', "http://google.com/");
debug_time(); // 2
$s->setSpeed(0);
debug_time(); // 3
$s->start();
debug_time(); // 4
var_export($s->getSpeed());
echo "\n";
debug_time(); // 5
$s->open('/');
debug_time(); // 6
$s->stop();
debug_time(); // 7
echo "done";
Here is the output for the slow test:
0 => 18:01:54.44488 (+ 0.00000)
1 => 18:01:54.45478 (+ 0.00990)
2 => 18:01:54.45645 (+ 0.00167)
3 => 18:02:54.97334 (+ 60.51688)
4 => 18:04:03.59346 (+ 68.62013)
NULL
5 => 18:05:04.11214 (+ 60.51867)
6 => 18:06:05.83747 (+ 61.72534)
7 => 18:07:06.63492 (+ 60.79744)
done
Here is the fast test, taken from the PHPUnit manual:
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';
class WebTest extends PHPUnit_Extensions_SeleniumTestCase
{
protected function setUp()
{
$this->setBrowser('*firefox');
$this->setBrowserUrl('http://google.com/');
}
public function testTitle()
{
$this->open('/');
$this->assertTitleEquals('Example Web Page');
}
}
The same here. Downgrade to Testing_Selenium-0.4.3 helped. Trying to find the reason. May be PHP5.3?