Skip to main content

PHPUnit

All PHPUnit tests start with a subclass of TestCase. You can then add one or more test case methods to that class, each of which must be public and start with test. In Codewars' PHP versions 7.4+, PHPUnit requires the name of the test class to end with Test.

Basic Setup

Solution

function add(int $a, int $b): int {
    return $a + $b;
}
function add(int $a, int $b): int {
    return $a + $b;
}

Tests

class AddTest extends TestCase {
    public function testAdd() {
        $expected = 3;
        $actual = add(1, 2);
        $this->assertSame($expected, $actual);
    }
}
class AddTest extends TestCase {
    public function testAdd() {
        $expected = 3;
        $actual = add(1, 2);
        $this->assertSame($expected, $actual);
    }
}

Assertions

Argument order

PHPUnit's assertions follow the parameter order ($expected, $actual), unlike most testing libraries which have $actual first.

Primitive equality

assertEquals has surprising behavior on primitives due to casting and loose equality. Here are sample comparisons that assertEquals considers equal:

$this->assertEquals("1", "001");
$this->assertEquals(1, "001");
$this->assertEquals(42, true);
$this->assertEquals("42", true);
$this->assertEquals(0, false);
$this->assertEquals([["foo" => 1]], [["foo" => "1"]]);
$this->assertEquals("1", "001");
$this->assertEquals(1, "001");
$this->assertEquals(42, true);
$this->assertEquals("42", true);
$this->assertEquals(0, false);
$this->assertEquals([["foo" => 1]], [["foo" => "1"]]);

assertSame fails all of the above checks, as one would likely want. assertEquals should be avoided due to its surprising behavior.

Object equality

It's tempting to use assertSame across the board, but its identity check is too strict for most object comparisons:

$expected = new stdClass;
$expected->foo = "foo";
$expected->bar = "42";
$actual = new stdClass;
$actual->foo = "foo";
$actual->bar = "42";
$this->assertSame($expected, $actual); // fails
$expected = new stdClass;
$expected->foo = "foo";
$expected->bar = "42";
$actual = new stdClass;
$actual->foo = "foo";
$actual->bar = "42";
$this->assertSame($expected, $actual); // fails

Here, using assertObjectsEqual, which calls a class' equals(self $other): bool method, might be a more appropriate approach.

Float equality

For float comparisons, assertEqualsWithDelta has similar unpredictable behavior as assertEquals:

$this->assertEqualsWithDelta(0.99, "1", 0.1); // passes
$this->assertEqualsWithDelta(99999, true, 0.1); // passes
$this->assertEqualsWithDelta(0.99, "1", 0.1); // passes
$this->assertEqualsWithDelta(99999, true, 0.1); // passes

Type-checking the arguments to assertEqualsWithDelta before calling it or implementing a custom delta assertion based on assertTrue may be a safer bet.