164

PHPUnit contains an assertEquals() method, but it also has an assertSame() one. At first glance it looks like they do the same thing.

What is the difference between the two? Why are they both specified?

Eddie C.
  • 918
  • 10
  • 16
Kevin Burke
  • 61,194
  • 76
  • 188
  • 305

8 Answers8

246

I use both sporadically, but according to the docs:

assertSame

Reports an error identified by $message if the two variables $expected and $actual do not have the same type and value."

And as you can see in the example below the above excerpt, they are passing '2204' and 2204, which will fail using assertSame because one is a string and one is an int, basically:

'2204' !== 2204
assertSame('2204', 2204) // this test fails

assertEquals

"Reports an error identified by $message if the two variables $expected and $actual are not equal."

assertEquals does not appear to take datatype into consideration so using the above example of 2204:

'2204' == 2204
assertEquals('2204', 2204) // this test passes

I just ran some unit tests against the above examples, and indeed they resulted in documented behavior.

Derick Alangi
  • 1,080
  • 1
  • 11
  • 31
Mike Purcell
  • 19,847
  • 10
  • 52
  • 89
  • 30
    assertEquals even thinks that `'0012' == '12'`. Even if both values are strings, they are converted to integers for the comparison! You should really use assertSame whenever you can. – marco-fiset Jun 28 '13 at 13:23
  • 2
    Unfortunately even assertEquals seems to be picky e.g. when comparing array properties and complains about string vs int then. – andig Dec 27 '13 at 13:26
  • 1
    Following marco-fiset's comment, note this behaviour is no longer the case since PHPUnit 4.0, see the [upgrade notes](http://phpunit.de/manual/current/en/appendixes.upgrading.html#appendixes.upgrading.phpunit-4-0). – Gras Double Jun 13 '14 at 21:36
  • @coviex Reference is cool, but the URL is wrong (because of closing square bracket)... can you please fix it? Thx! – Christian Aug 11 '16 at 09:48
  • 4
    Important note on **comparing objects** with `assertSame()`. Reports an error identified by $message if the two variables $expected and $actual do not reference the same object. https://phpunit.de/manual/current/en/appendixes.assertions.html#appendixes.assertions.assertSame – coviex Aug 15 '16 at 16:07
  • I just want to point out something strange. When I do `assertEquals(['test'], 'test)`, I get an error message saying `'test' does not match expected type "array"`. So it looks like `assertEquals` does take datatype into consideration for some cases? – Cave Johnson Oct 31 '17 at 17:20
  • @KodosJohnson I think it's intended to assert that `$actual == $expected` and the result corresponds to comparison using using the equality (double-equals) operator with standard PHP type juggling - which does not convert strings to arrays or vice versa (`'test' !== ['test']` but also `'test' != ['test']`). – Jake Feb 18 '18 at 23:20
  • `assertSame` also checks if the two objects refers to the same object if you are passing two variables. – Farooq Ahmed Khan Sep 04 '19 at 13:03
  • 2
    all this makes me wonder why we don't just use assertTrue with === and == so we know what is happening without google. – Brad Mar 09 '21 at 02:40
48

When it comes to objects comparison:

assertSame

Can only assert if two objects are referencing the same object instance. So even if two separate objects have for all of their attributes exactly the same values, assertSame() will fail if they don't reference the same instance.

$expected = new \stdClass();
$expected->foo = 'foo';
$expected->bar = 'bar';

$actual = new \stdClass();
$actual->foo = 'foo';
$actual->bar = 'bar';

$this->assertSame($expected, $actual); // FAILS

assertEquals

Can assert if two separate objects match their attribute values in any case. So it's the method suitable for asserting object match.

$this->assertEquals($expected, $actual); // PASSES

Reference

Eddie C.
  • 918
  • 10
  • 16
Grigoreas P.
  • 2,452
  • 25
  • 19
  • 11
    While this answer isn't comprehensive (it only covers objects), it's exactly what I needed to know. Thanks! :) – rinogo Jul 12 '16 at 01:31
  • 2
    Yea, but `assertEquals()` compares `null` and `""` and even `0` as equal. – Danon Aug 09 '20 at 11:07
  • 2
    Just discovered a hidden bug that was caused by this (tested for `0.0` instead of `null`). I will completely avoid `assertEquals` from now on. – CunningFatalist May 17 '21 at 08:55
  • "So it's the method suitable for asserting object match." -- only if you don't mind surprising object property comparisons like `42` and `true` being considered equal. `assertObjectsEqual` seems better, invoking the objects' `equals()` method. – ggorlen Jun 18 '22 at 22:32
21
$this->assertEquals(3, true);
$this->assertSame(3, true);

The first one will pass!

The second one will fail.

That is the difference.

I think you should always use assertSame.

David Winiecki
  • 4,093
  • 2
  • 37
  • 39
bronze man
  • 1,470
  • 2
  • 15
  • 28
  • 2
    I just had this gotcha during test driven development. test passed, assumed the value 3 was being returned but actually true was returned. interestingly $this->assertEquals('3', true); fails. – dwenaus Jun 06 '16 at 19:43
  • Good post, but "I think you should always use assertSame." [isn't the right conclusion for objects](https://stackoverflow.com/a/37653597/6243352) 99% of the time. – ggorlen Jun 18 '22 at 20:23
3

As it's been said before, assertSame reports an error if the two elements do not share type and value but it's also important to note this from the documentation:

Reports an error identified by $message if the two variables $expected and $actual do not reference the same object.

So this test would fail too even though they share type and value:

class SameTest extends TestCase
{
    public function testFailure()
    {
        $this->assertSame(new stdClass, new stdClass);
    }
}
ggorlen
  • 44,755
  • 7
  • 76
  • 106
1

Moreover,

// Passes
$this->assertSame("123.", "123.");
$this->assertEquals("123.", "123");
// Fails
$this->assertSame("123.", "123");
GogromaT
  • 483
  • 1
  • 5
  • 20
1

Never use assertEquals (and family)*

* unless arguments were first manually type-checked, or you're fully aware of the behavior and are prepared for tests to pass when it treats, say, true and 42, as equal--I can't imagine why one would tolerate the risk of extremely subtle false positives in critical testing code.

For primitive values (other than floats) and arrays

Use assertSame. The behavior is predictable. "42" and true are not equal.

For floats

Avoid if possible by using integers. If you must compare floats, assertEqualsWithDelta is as broken as assertEquals:

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

The implementation of assertEqualsWithDelta uses an inaccurate approach, as Comparing Floating Point Numbers describes: abs($this->value - $other) < PHP_FLOAT_EPSILON;, so you might want to write a custom function.

Or type-check before calling assertEqualsWithDelta to avoid the more immediate issues:

function is_number($v) {
    return is_float($v) || is_int($v);
}

class FloatTest extends TestCase {
    private function assertClose(
        $expected,
        $actual,
        $delta = 1e-7
    ): void {
        if (!is_number($expected)) {
            $type = gettype($expected);
            throw new Error("\$expected type $type was not a number");
        }
        else if (!is_number($actual)) {
            $type = gettype($actual);
            throw new Error("\$actual value $type was not a number");
        }
        else if (!is_number($delta)) {
            $type = gettype($delta);
            throw new Error("\$delta value $type was not a number");
        }
    
        $this->assertEqualsWithDelta($actual, $expected, $delta);
    }

    public function testFloats() {
        $this->assertClose(2, "2"); // fails as it should
    }
}

For objects

Use a custom equals(self $other): bool function and assertObjectEquals.

If all else fails

You can't go wrong with assertTrue, the downsides being a lack of semantic appropriateness and poor default error message.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
0

assertSame() == Tests that if the actual output and the expected parameter are same.

that is :

$this->assertSame('$expected','$expected');

or

$this->assertSame('100','100');

assertEquals == If we see with respect to a website page, i have a page which has 2 'table' so when i run assertEquals i will check its count that the 'table' are 2 by using a count function. Eg:

$this->assertEquals(2, $var->filter('table')->count()); 

Here we can see that assertEquals checks that there are 2 tables found on the web page. we can also use divisions found on the page using '#division name' inside the bracket.

Eg 2:

public function testAdd()
{
    $calc = new Calculator();

    $result = $calc->add(30, 12);

    // assert that our calculator added the numbers correctly!
    $this->assertEquals(42, $result);
}
AbcAeffchen
  • 14,400
  • 15
  • 47
  • 66
Arpan Buch
  • 1,380
  • 5
  • 19
  • 41
0

As previously mentioned, assertEquals() is primarily about an interpreted value, be it by type juggling or an object with an __magic presentation method (__toString() for example).

A good use case for assertSame() is testing a singleton factory.

class CacheFactoryTest extends TestCase
{
    public function testThatCacheFactoryReturnsSingletons()
    {
        $this->assertSame(CacheFactory::create(), CacheFactory::create());
    }
}
Richard A Quadling
  • 3,769
  • 30
  • 40