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.
Related
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.
Prior to PHP 7.2 using count() on a scalar value or non-countable object would return 1 or 0.
For example: https://3v4l.org/tGRDE
var_dump(count(123)); //int(1)
var_dump(count(new stdclass)); //int(1)
var_dump(count('hello world')); //int(1)
var_dump(count(null)); //int(0)
In the updates to PHP 7.2+, using count() as demonstrated above will emit a warning message.
An E_WARNING will now be emitted when attempting to count() non-countable types (this includes the sizeof() alias function).
Warning: count(): Parameter must be an array or an object that implements Countable
[sic]
As a result many popular Frameworks will elevate the E_WARNING and throw an Exception instead.
[ErrorException] count(): Parameter must be an array or an object that implements Countable
The error elevation behavior was also commented on by the PHP developers.
Environments that display warnings or convert them to more severe errors/exceptions would be affected, but this should just bring attention to a bug in the code.
How can the previous behavior of count() be achieved in PHP 7.2+, that does not emit an E_WARNING, without modifying the error reporting setting and without using #count()?
As we discussed, there are multiple ways to achieve the original functionality of count() and not emit an E_WARNING.
In PHP 7.3 a new function was added is_countable, specifically to address the E_WARNING issue and the prevalence of applications adopting is_array($var) || $var instanceof \Countable in their code.
In PHP 7.2, a Warning was added while trying to count uncountable
things. After that, everyone was forced to search and change their
code, to avoid it. Usually, the following piece of code became
standard:
if (is_array($foo) || $foo instanceof Countable) {
// $foo is countable
}
https://wiki.php.net/rfc/is-countable
Custom Function Replacement
For that reason it seems the best method of resolving the issue, is to perform the same functionality that PHP is doing with is_countable and creating a custom function to ensure compliance with the original functionality of count.
Example https://3v4l.org/8M0Wd
function countValid($array_or_countable, $mode = \COUNT_NORMAL)
{
if (
(\PHP_VERSION_ID >= 70300 && \is_countable($array_or_countable)) ||
\is_array($array_or_countable) ||
$array_or_countable instanceof \Countable
) {
return \count($array_or_countable, $mode);
}
return null === $array_or_countable ? 0 : 1;
}
Result
array: 3
string: 1
number: 1
iterator: 3
countable: 3
zero: 1
string_zero: 1
object: 1
stdClass: 1
null: 0
empty: 1
boolt: 1
boolf: 1
Notice: Undefined variable: undefined in /in/8M0Wd on line 53
undefined: 0
Shim is_countable() function
Using the above replacement function, it is also possible to shim is_countable in PHP <= 7.2, so it is only used when needed, with minimal overhead.
Example https://3v4l.org/i5KWH
if (!\function_exists('is_countable')) {
function is_countable($value)
{
return \is_array($value) || $value instanceof \Countable;
}
}
function countValid($array_or_countable, $mode = \COUNT_NORMAL)
{
if (\is_countable($array_or_countable)) {
return \count($array_or_countable, $mode);
}
return null === $array_or_countable ? 0 : 1;
}
Ignore count() Warnings
As the functionality of count() has not changed and not did not typically emit warnings in the past. An alternative to using a custom function, is to ignore the warning outright by using the # Error Control Operator
Warning: This approach has the impact of treating undefined variables as NULL and not displaying Notice: Undefined variable:
message.
Example https://3v4l.org/nmWmE
#count($var);
Result
array: 3
string: 1
number: 1
iterator: 3
countable: 3
zero: 1
string_zero: 1
object: 1
stdClass: 1
null: 0
empty: 1
boolt: 1
boolf: 1
---
Undefined: 0
Replace count() using APD extension
As for replacing the internal PHP function count(). There is a PECL extension APD (Advanced PHP Debugger), that allows for override_function that works on core PHP functions. As the extension name suggests, it is technically meant for debugging, but is a viable alternative to replacing all instances of count for a custom function.
Example
\rename_function('count', 'old_count');
\override_function('count', '$array_or_countable,$mode', 'return countValid($array_or_countable,$mode);');
if (!\function_exists('is_countable')) {
function is_countable($value)
{
return \is_array($value) || $value instanceof \Countable;
}
}
function countValid($array_or_countable, $mode = \COUNT_NORMAL)
{
if (\is_countable($array_or_countable)) {
return \old_count($array_or_countable, $mode);
}
return null === $array_or_countable ? 0 : 1;
}
The problem is that calling count() on a scalar or object that doesn't implement the Countable interface returns 1, which can easily hide bugs.
Given the following:
function handle_records(iterable $iterable)
{
if (count($iterable) === 0) {
return handle_empty();
}
foreach ($iterable as $value) {
handle_value($value);
}
}
Passing a Generator that yields nothing would not call handle_empty() nor handle_value().
Also, no indication would be given that neither were called.
By default, this will still return 1, though will additionally log a warning. If anything, this warning will bring attention to potential bugs in the code.
See Counting Non-Countables for further information.
You can solve it by using "??"-operator. If the left side is null, the right side will be used. So as we have a empty array, our result will be zero.
count(null ?? [])
Another way would be to typecast it as an array.
count((array) null)
In the PHP documentation for json_decode it says it can return TRUE,FALSE,NULL.
Could some help me understand when it would return FALSE? I understand invalid JSON will return NULL, but when would the other two be returned if not the actual JSON value?
Thanks
JSON format definition clearly shows all possible values and their representations:
A value can be a string in double quotes, or a number, or true or
false or null, or an object or an array.
Both objects and arrays have special syntax in JSON representation (wrapped in {} and [] respectively), so they can't be mixed up with false in any case. The same goes with string - it's wrapped in "" (double quotation marks). As for Numbers, they have to contain at least one digit - so cannot be confused with false (and true and null) too.
So that leaves us with the only case: when json_encode processes an object having redefined its JSON representation. For example (PHP 5.4+):
class FalsyFoo implements JsonSerializable {
public $foo;
public function __construct($f) {
$this->foo = $f;
}
public function jsonSerialize() {
return false;
}
}
$f = new FalsyFoo(true);
$fj = json_encode($f);
var_dump( $fj ); // string(5) 'false'
var_dump( json_decode($fj) ); // bool(false)
Technically, we still work with false value here, but the source is obviously different.
If you're still not convinced, check the source code of json_decode, which calls php_json_decode_ex after checking the arguments. This, in turn, calls parse_JSON_ex first, which operates over the predefined state transition table; the latter has only one set of states leading to false value as result. If this call fails somehow, value is checked directly:
if (str_len == 4) {
if (!strcasecmp(str, "null")) {
/* We need to explicitly clear the error
because its an actual NULL and not an error */
jp->error_code = PHP_JSON_ERROR_NONE;
RETVAL_NULL();
} else if (!strcasecmp(str, "true")) {
RETVAL_BOOL(1);
}
} else if (str_len == 5 && !strcasecmp(str, "false")) {
RETVAL_BOOL(0);
}
... and that's the only case when return_value is set to boolean.
The documentation says that values true, false and null (case-insensitive) are returned as TRUE, FALSE and NULL respectively. This means that if the booleans true orfalse are in the object to be encoded, they will be shows as TRUE or FALSE, and the same for null. For example:
json_decode('["hello",true]');
would return:
["hello",TRUE]
It doesn't mean that json_decode will return values of true, false, or null
I am using PHPUnit to test my code But when i use assertTrue phpunit behaves expectly. Is this normal beahviour of phpunit or not. I got the following error.
Failed asserting that 1 is true.
1 is not a "real" true value. You can try this :
true == 1 // return true
true === 1 // return false
false == null // return true
false === null // return false
PHPUnit use === in assertTrue. So if you do assertTrue(1);, PHPUnit just stop because the assertion is false.
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.