はじめに
オブジェクト指向を学び初めたばかりです。
オブジェクトが絡むエラーがいまいち理解できなかったので記事にしました。
やりたいこと
assertSameメソッドのexpectedもactualも同じオブジェクトnew Card('クラブ', '10') なのにエラーになる。それを解消したい。
//$dealer->hands->getFirstCard());をすると
//new Card('クラブ', '10')が取得できていることをテストしたい
//テストコード
public function testGetFirstCard()
{
$dealer = new Dealer('ディーラー');
$dealer->hands->cards = [new Card('クラブ', '10'), new Card('クラブ', 'A')];
$this->assertSame(new Card('クラブ', '10'), $dealer->hands->getFirstCard());
}
//$dealer->hands->getFirstCard()で$dealer->hands->cardsプロパティのnew Card('クラブ', '10')を取得している
やったこと/解決策
そもそも「$cardsプロパティの0番目のnew Card('クラブ', '10')が取得できているか」を確認したいテスト
なので、期待値に「new Card('クラブ', '10')」を定義するのではなく、「$cardsプロパティのnew Card('クラブ', '10')」にアクセスするように修正した。⇒ passした
public function testGetFirstCard()
{
$dealer = new Dealer('ディーラー');
$dealer->hands->cards = [new Card('クラブ', '10'), new Card('クラブ', 'A')];
$this->assertSame($dealer->hands->cards[0], $dealer->hands->getFirstCard());
}
//$dealer->hands->cards[0]で「$cardsプロパティのnew Card('クラブ', '10')」にアクセス
原因
このエラーメッセージは、expectedとactualのオブジェクトが同じオブジェクトを参照していることを期待するアサーションで失敗したことを示していた。
どちらもnew Card('クラブ', '10')ですが、expectedとactualで参照している先が異なっていた。
//$cardsプロパティ
$dealer->hands->cards = [new Card('クラブ', '10'), new Card('クラブ', 'A')];
//両方とも$cardsプロパティのnew Card('クラブ', '10')を取得
$dealer->hands->getFirstCard()
$dealer->hands->cards[0]
//[NG]ここのexpectedのnew Card('クラブ', '10')は「ただのnew Card('クラブ', '10')」
$this->assertSame(new Card('クラブ', '10'), $dealer->hands->getFirstCard());
//[OK]ここのexpectedのnew Card('クラブ', '10')は「$cardsプロパティのnew Card('クラブ', '10')」
$this->assertSame($dealer->hands->cards[0], $dealer->hands->getFirstCard());
expectedもactualと全く同じオブジェクトを参照する必要があるため、$cardsプロパティのnew Card('クラブ', '10')オブジェクトにアクセスしないといけなかった。
参考
chatGPT
このエラーメッセージは、2つのオブジェクトが同じオブジェクトを参照していることを期待するアサーションで失敗したことを示しています。つまり、2つの変数が異なるインスタンスを参照しているため、アサーションによってエラーが発生しました。
この問題を解決するには、テスト内で2つの変数が同じインスタンスを参照するように確認する必要があります。
PHPUnitドキュメント
全く同じオブジェクトを参照する必要がありと記載
https://docs.phpunit.de/en/10.1/assertions.html?highlight=Failed asserting that two variables reference the same object.#assertsame
Reports an error identified by $message if the two variables expected and actual do not reference the same object.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class SameWithObjectsTest extends TestCase
{
public function testFailure(): void
{
$this->assertSame(new stdClass, new stdClass);
}
}
// There was 1 failure:
// 1) SameWithObjectsTest::testFailure
// Failed asserting that two variables reference the same object.
stuck overflow
2つのオブジェクトが同じオブジェクトインスタンスを参照している場合にのみ、Passする。したがって、2つの別々のオブジェクトがすべての属性でまったく同じ値を持つ場合でも、同じインスタンスを参照していなければassertSame()は失敗します。
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
補足〜assertSameとassertEqualsの違い
assertSameは厳密な比較が行われている(型、値、オブジェクトの場合は参照先も比較されている)
assertSame('2204', 2204) // this test fails
assertEqualsは緩やかな比較が行われている
assertEquals('2204', 2204) // this test passes
最後に
assertSameはなぜオブジェクトの参照先までチェックする?と追加で気になったので、いろいろググって見たけど理由は見つけられませんでした。
まあでも、記事に残すことが出来て理解も深まりスッキリしました\(^o^)/