PHPUnitでよく使うassertion

# 注意事項
この記事では、PHPUnit 11.5時点のassertionについて解説しています。以降のバージョンでは変更になる可能性があることを、ご了承ください。
# はじめに
こちらは、[日本CTO協会24卒 Advent Calendar 2024](https://qiita.com/advent-calendar/2024/ctoa-24) 23日目の記事です。

こんにちは。24卒バックエンドエンジニアのwind111です。

皆様、テストは書いてますか?

自分は、業務でPHPを用いて新機能やリファクタリングに取り組んでおり、その中で自分が書いたコードをテストする機会が増えました。
今回は、PHPテストツールの代名詞であるPHPUnitでよく使うassertionを紹介します。

この記事を通して、PHPUnitの基本的なassertionを知るだけでなく、テストコードを書く楽しさもお伝えできれば幸いです。

## PHPUnitとは?
PHPUnitは、PHPでユニットテストを行うための標準的なフレームワークです。シンプルな構文と豊富な機能を備えており、単体テストや回帰テストまで幅広くサポートしています。
また、外部サービスAPIをモック化することも可能で、AWSやGoogle Cloudなどのクラウドサービスを利用しているシステムのテストにも活用できます。
## assertの基礎知識
assertion（アサーション）とは、テストコードにおいて「期待値」と「実際の結果」を比較し、
テストの成否を判定する仕組みです。

以下のようにassertionの例を示します。
```php
<?php

declare(strict_types=1);
namespace Application\CalculateBoard\Service;

use \PHPUnit\Framework\TestCase;

class CalculateBoardServiceTest extends TestCase
{
    public function calculateTest(){
        $this->assertEquals(2, 1 + 1); // 成功
        $this->assertEquals(3, 1 + 1); // 失敗
    }
}
```
この状態でテストを実行すると、失敗したテストでは以下のようなメッセージが表示されます。

```
Testing started at 15:36 ...

Failed asserting that 2 matches expected 3.

path/to/CalculateBoardServiceTest.php:8
 
Time: 97 ms, Memory: 4.00 MB

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.
```
このように、コードの挙動やテストの意図を見直して，必要に応じて修正を加えることでより正確で堅牢なコードを実現できます。

## assertionの種類
ここでは、よく使うassertionについて紹介します。

### 正誤をチェックする関数
正誤をチェックするには、`assertTrue`と`assertFalse`を使用します。
この関数は、主にbooleanで特定の条件が期待通りの結果を返すかどうかを簡単に検証する際に便利です。
```php
<?php

declare(strict_types=1);
namespace Application\Service;

use PHPUnit\Framework\TestCase;

class BooleanCheckTest extends TestCase
{
    public function testUserConditions()
    {
        $this->assertTrue(isExists($userId));
        $this->assertFalse(isExists($userId));
    }
}
```

### 値の一致をチェックする関数
値が一致するかチェックする関数には、`assertEquals` と `assertNotEquals`、
値と型の一致をチェックする`assertSame`と`assertNotSame`を使用します。
`assertEquals` は値のみを比較し、`assertSame `は値と型、並び順を厳密に比較します。

```php
<?php

declare(strict_types=1);
namespace Application\Service;

use PHPUnit\Framework\TestCase;

class ValueCheckTest extends TestCase
{
    public function testValueEquality()
    {
        $this->assertEquals('123', 123); // 成功（値のみ比較）
        $this->assertSame('123', 123);  // 失敗（型も比較）
    }
}
```

### 型をチェックする関数
型のチェックには、様々な型に応じた関数が存在します。
- `assertIsString`, `assertIsNotString`
- `assertIsInt`, `assertIsNotInt`
- `assertIsArray`, `assertIsNotArray`

など、変数の型安全性を検証する際に有効です。
arrayのチェックに使用する機会が多いと思いますが、その他はPHPStanなど静的解析を行っている場合、使用する場面は少ないかもしれません...
```php
<?php

declare(strict_types=1);
namespace Application\Service;

use PHPUnit\Framework\TestCase;

class TypeCheckTest extends TestCase
{
    public function testTypeChecks()
    {
        $this->assertIsInt(getUserId($username));
        $this->assertIsString(getUserName($userId));
    }
}
```

### 配列のキーをチェックする関数
配列のキーをチェックする関数には、arrayHasKey`と`assertArrayNotHasKey`を使用します。
この関数は、特定のキーが存在するかどうかを確認するために使用します。
連想配列を扱う、必要なデータが含まれているかを検証する際に便利です。
```php
<?php

declare(strict_types=1);
namespace Application\Service;

use PHPUnit\Framework\TestCase;

class AssociativeArrayKeyCheckTest extends TestCase
{
    public function testArrayKeyChecks()
    {
        $array = ['key1' => 'value1', 'key2' => 'value2'];

        $this->assertArrayHasKey('key1', $array);
        $this->assertArrayNotHasKey('key3', $array);
    }
}

```

### classの一致をチェックする関数
`assertInstanceOf`や`assertNotInstance`を使用します。
この関数は、特定のclassまたはInterfaceのインスタンスであるかを確認するために使用します。
主に、DTOや、Interfaceでレスポンスが返却されることを検証する際に便利です。

```php
<?php

declare(strict_types=1);
namespace Application\Service;

use PHPUnit\Framework\TestCase;

class InstanceCheckTest extends TestCase
{
    public function testInstanceCheck()
    {
        $userData = getUserById($userId);
        
        $this->assertInstanceOf(UserDto::class, $userData);
        $this->assertNotInstanceOf(AdminDto::class, $userData);
    }
}
```

### 指定した例外の一致をチェックする関数
この関数は、`expectException` を使用して特定の例外がスローされるかどうかを確認します。
try-catchを使用した際に、特定の例外がcatchされるかを確認する際に便利です。
```php

declare(strict_types=1);
namespace Application\Service;

use Exception;
use PHPUnit\Framework\TestCase;

class ExceptionCheckTest extends TestCase
{
    public function testExceptionCheck()
    {
        $this->expectException(Exception::class);

        $user = getUser($invalidUser);
    }
}
```

## まとめ
PHPUnitは、PHPでのテスト開発において非常に強力なツールです。本記事では基本的なassertionを紹介しましたが、他にも多くの機能があります。
これを機に、テストコードを書く習慣をつけてみてはどうでしょうか？

この記事が皆様の参考になれば幸いです。

## 外部リンク
[PHPUnit 11.5 Manual]('https://docs.phpunit.de/en/11.5/')