laravelでテストしてみた！

webエンジニアとして働き始め半年のペーペーな私ですが、
自分が実装したコードで出す値の整合性確認をテストする必要があり、テストコードでそれを実施することになりました。

テストに対しては謎の苦手意識があり、
「テストコードが絶対正しいわけじゃなくない！？テストコードのテストはどーすんの！？」
「ロジックが正しいかどうかなんてどうやってわかるの！！？」
なんて思っていたんですが、
実際やって見るとなんの事はなく
あーもっと早くこれやっとけばよかったなと思いました。。

自分の後学のためにも、書いてみたテストをまとめてみました。

# やりたいこと

- ある店舗の売り上げの前年比を計算しy_o_yというテーブルに保存する、という処理を実行するバッチのテスト

このバッチで実装されたロジックが正しいものであるか確認するため、y_o_yテーブルの中の前年比データが正しいものなのかをテストコードを用いて確認します。

前年比は、今年の対象月日の値 / 去年の対象月の合計値の日割り値　* 100
で算出します。

# 前提知識

- 店舗の情報が入ったstoresテーブル
     - id ... このテーブル上でのID
     - store_name ... 店舗の名前
     - created_at ... レコード作成時間
     - updated_at ... レコード最新更新時間


```
+----+-----------------+---------------------+---------------------+
| id | store_name      | created_at          | updated_at          |
+----+-----------------+---------------------+---------------------+
```


- 店舗ごとの1日の売り上げが入ったprofitsテーブル
     - id ... このテーブル上でのID
     - store_id ... 対象店舗のstores.id
     - target_dt ... 対象年月（YYYY-MM-DD）
     - year ... 対象年
     - month ... 対象月
     - day ... 対象日
     - profit ... 売り上げ
     - created_at ... レコード作成時間
     - updated_at ... レコード最新更新時間

```
+----+-----------+-----------------+------------+------------+---------+------------+---------------------+---------------------+
| id | store_id 　　| target_dt       | year　　　　　  　　　 | month      | day　　　　　　　　 | profit　　　　　　　　 | created_at          | updated_at          |
+----+-----------+-----------------+------------+------------+---------+------------+---------------------+---------------------+
```

- バッチで計算した売り上げ前年比を保存するy_o_yテーブル
     - id ... このテーブル上でのID
     - store_id ... 対象店舗のstores.id
     - target_dt ... 対象年月（YYYY-MM-DD）
     - ratio ... 前年比
     - created_at ... レコード作成時間
     - updated_at ... レコード最新更新時間


```
+----+-----------+-----------------+------------+---------------------+---------------------+
| id | store_id 　　| target_dt       | ratio　　　　　　　 　　　| created_at          | updated_at          |
+----+-----------+-----------------+------------+---------------------+---------------------+
```

# テストコード

laravelのartisanコマンドでテストファイルを作り、実装をします。

```
// Featureディレクトリにテストを生成する
php artisan make:test LaravelTest

// Unitディレクトリにテストを生成する
php artisan make:test LaravelTest --unit
``` 

```LaravelTest.php
<?php

namespace Tests\Feature;

use App\Models\YoY;
use App\Models\Profit;
use Carbon\Carbon;
use Tests\TestCase;
use Illuminate\Support\Facades\DB;


class LaravelTest extends TestCase
{

    /**
     * A basic test example.
     *
     * @return void
     */

    // store_id,期間を設定する
    public function test()
    {
        $this->assertYoY(1, '2018-01-01', '2018-01-31');
    }

    private function assertYoY($storeId, $fromDate, $toDate)
    {
        $fromDate = Carbon::parse($fromDate);
        $toDate = Carbon::parse($toDate);

        // バッチで計算して入れた値
        $ratios = YoY::select('store_id', 'target_dt', 'ratio')
            ->where('store_id', $storeId)
            ->whereBetween('target_dt', [$fromDate, $toDate])
            ->get()
            ->keyBy('target_dt')
            ->toArray();

        // 今年の対象月日の売り上げを取得
        $profitsPerDay = Profit::select('store_id', 'target_dt', 'profit')
            ->whereBetween('target_dt', [$fromDate, $toDate])
            ->where('store_id', $storeId)
            ->get()
            ->keyBy('target_dt')
            ->toArray();

        $days = date('t', mktime(0, 0, 0, $fromDate->copy()->month, 1, $fromDate->copy()->year - 1));

        // 去年の対象月の日割り値を取得
        $lastProfitsPerDay = Profit::select([
            'store_id',
            'target_dt',
            'month',
            DB::raw('sum(profit) / ' . $days . ' as ratio'),
        ])
            ->where('year', $fromDate->copy()->year - 1)
            ->where('month', $fromDate->copy()->month)
            ->where('store_id', $storeId)
            ->groupBy('store_id', 'month')
            ->first();

        // 今年の対象月日の売り上げと去年の対象月の日割り値を比較し前年比を取得
        foreach ($profitsPerDay as $target_dt => $cons) {
            $actuals[$target_dt]['ratio'] = $lastProfitsPerDay['ratio'] == 0 ? 0 : $profitsPerDay[$target_dt]['ratio'] / $lastProfitsPerDay['ratio'] * 100;
        }

        // バッチで計算して出した前年比とテストコードで出した前年比を比較（小数点第2位まで四捨五入）
        foreach ($actuals as $target_dt => $actual) {
            if (isset($ratios[$target_dt]['ratio']) && isset($actual['ratio'])) $this->assertEquals(round($ratios[$target_dt]['ratio'], 2), round($actual['ratio'], 2));
        }

        $ratios = [];
        $actuals = [];

      }

}

```

自分で直接テストするときにしてること（今年分と去年分DBから値を持ってきて、比較して前年比を出す）をコードにしただけです。

バッチで実装されてるロジックとほぼ同じロジックでデータを取ってきて、laravelで用意されているassertEqualsメソッドで比較をします。

このテストを実行するときは、このコード上のtestメソッドで値を確かめたい店舗のID,期間を設定し、
コマンドライン上で実行をします。


```
root@44e398b46609:/var/www# ./vendor/bin/phpunit tests/Feature/LaravelTest.php
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.
..                                                                  2 / 2 (100%)
Time: 33.85 seconds, Memory: 18.00MB
OK (2 tests, 18 assertions)
```

問題なければ上記のように表示されます。

# テストコード書いたときに気をつけていたこと

## シンプルに書く

テストコード自体にバグがあるとテストになりません。
テストコードのテストが必要、なんてことにならないようにパッと見て何をやっているかわかるくらいシンプルに実装するよう心がけました。

## 全てではなく範囲を定めた値をテストする

バッチでは全店舗全期間取得しデータをテーブルに入れていますが、
テストではどの店舗のどの期間のデータがおかしいのか把握するために、店舗や期間を定めた範囲でテストするような実装にしました。

# 書いてみた結果、、

自分でいちいち計算して確認するとしたらすごく時間がかかってしまいますが
このテストを一度作ってしまえば多くのデータを短い時間で確認することができました！
ほんとなんで今までやらなかったんだろ、、

今回はデータの値の整合性を確認するようなテストを実装しましたが、
画面の動作確認のテストも書いてみようと思います！