Spring Boot × JUnit：Entityの単体テストを基礎から解説！

# はじめに
先日Spring Bootを用いたアプリをAWSにデプロイしました。
各層(Controller,Service,Repository,Entity)に単体テストをしており、この記事ではEntityにフォーカスしたJUnitによるテスト方法を解説していきます。
# テストで使用するアプリ
今回のテストでは筆者がSpring BootとVue.jsで作ったアプリ和楽知屋(わがっちゃ)を使用します。
単体テストではJUnitやMockitoを使い、投稿(アプリ内で「ツツメキ」と呼んでいます)のCRUD機能で関数が正常に動作するかを確認しました。
アプリの詳細は[こちら](https://github.com/MasaNakamura-ctrl/WaGotcha)を参照してください。
このアプリではMavenを使っているのでテストを実行する時は下記のコマンドを使っています。
Gradleなど違うものを使っている場合は適宜読み替えるようにしてください。
```
mvn test
```
今回の記事ではEntity層のテストを行なっていきます。
(今後の進捗で記事とGitで実装内容が変わっている場合もあるので予めご了承ください。)
# Entity層の役割とテスト対象の概要
Entityクラスはデータベースのテーブル構造と1対1で対応しており、アノテーションを通じて各カラムとJavaフィールドがマッピングされています。
今回テストを行うTsutsumekiエンティティは、以下のようなテーブル構造に基づいています。

| カラム名 | 型 | 最大文字数 | 主キー | 必須 | 概要 |
| --- | ----------- | ----------- | ----------- | ----------- | ----------- |
| id | INTEGER |  | ○ | ○ | ツツメキ毎に割り当てられるID |
| tsutsumeki | VARCHAR | 140 |  | ○ | 各ツツメキの投稿内容 |

実際のコードでは上記のテーブル構造を踏まえ、各フィールドに対しゲッターとセッターを定義し、オブジェクト同士の比較やハッシュマップなどの利用も想定して"equals()"と"hashCode()"も実装しています。
```Java:Tsutsumeki.java
package com.example.api.tsutsumeki;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Tsutsumeki {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String tsutsumeki;
    public Tsutsumeki(){
    }
    public Tsutsumeki(int id, String tsutsumeki){
        this.id = id;
        this.tsutsumeki = tsutsumeki;
    }
    public int getId(){
        return id;
    }
    public void setId(int id){
        this.id = id;
    }
    public String getTsutsumeki(){
        return tsutsumeki;
    }
    public void setTsutsumeki(String tsutsumeki){
        this.tsutsumeki = tsutsumeki;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        result = prime * result + ((tsutsumeki == null) ? 0 : tsutsumeki.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Tsutsumeki other = (Tsutsumeki) obj;
        if (id != other.id)
            return false;
        if (tsutsumeki == null) {
            if (other.tsutsumeki != null)
                return false;
        } else if (!tsutsumeki.equals(other.tsutsumeki))
            return false;
        return true;
    }
}
```
下記が対するテストコードになります。
```Java:TsutsumekiTest.java
package com.example.api.tsutsumeki;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import org.junit.jupiter.api.Test;

public class TsutsumekiTest {
    @Test
    void tsutsumekiFields(){
        Tsutsumeki req = new Tsutsumeki();
        req.setId(1);
        req.setTsutsumeki("Success!");

        assertEquals(1,req.getId());
        assertEquals("Success!",req.getTsutsumeki());
    }

    @Test
    void testEqualAndHashSuccess(){
        Tsutsumeki test1 = new Tsutsumeki(1, "Test");
        Tsutsumeki test2 = new Tsutsumeki(1, "Test");

        // equals
        assertEquals(test1,test2);

        // hashCode
        assertEquals(test1.hashCode(),test2.hashCode());
    }

    @Test
    void testEqualAndHashFailure(){
        Tsutsumeki test1 = new Tsutsumeki(1, "Test");
        Tsutsumeki test2 = new Tsutsumeki(2, "Test");

        // equals
        assertNotEquals(test1,test2);

        // hashCode
        assertNotEquals(test1.hashCode(),test2.hashCode());
    }
}
```
テストの進め方について詳しく触れていきます。
## Tsutsumekiクラス
Tsutsumekiクラスに対してはtsutsumekiFields()にてテストをしています。
ここではreqという名前のTsutsumekiを定義し、そこにidとtsutsumekiというカラムに該当する仮データを割り当てます。(idを1とし,tsutsumekiは文字列"Success!"とします)
この場合、**仮データを割り当てられたreqがTsutsumekiとして持っている処理getId()とgetTsutsumeki()から取得したものが仮データと一致してないとおかしい**ということになります。
きちんと取得できているかを確認するために、idとtsutsumekiそれぞれにassertEquals()を使います。
```
assertNotEquals(期待値としてセッターで割り当てたもの,ゲッターで取得した実際の値);
```
これが一致していればテストは成功となります。
## equals,hashCode()
equalsとhashCode()ではTsutsumekiを2つ定義し、それぞれに始めからidとtsutsumekiをセットしてしまいます。
そしてオブジェクトとハッシュコードがそれぞれ一致しているかをassertEquals()を使って判定します。
敢えて合致してないパターンも作ってそれに合った反応をさせるにはassertNotEquals()を使用します。
# 終わりに
今回の記事ではEntityクラスのテストについて解説をしました。
この層で軸となるのは下記の2点となります。
- ゲッターの処理を通してフィールドにセットした値を正しく取得できるか
- equals()やhashCode()の挙動が期待通りであるか

これらの確認手段としてassertEquals()とassertNotEquals()を使用しました。
Entity層以外のテストコードも以下のリポジトリにまとめています。
よろしければ参考にしていただけると幸いです。
👉[リポジトリを見る](https://github.com/MasaNakamura-ctrl/WaGotcha/tree/main/api/src/test/java/com/example/api/tsutsumeki)