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); // failsHere, 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); // passesType-checking the arguments to assertEqualsWithDelta before calling it or implementing a custom delta assertion based on assertTrue may be a safer bet.