Fusic Tech Blog

Fusion of Society, IT and Culture

PESTを使ったメンテナブルな組み合わせテストを実装してみる
2021/11/17

PESTを使ったメンテナブルな組み合わせテストを実装してみる

こんにちは、Fusic武末と申します。

最近、リンゴ丸かじりにハマっています。

今日は注目されつつあるPHPテストフレームワーク「PEST」でテスト効率化の検証をやってみたいと思います。

検索条件等の複数のデータの組み合わせで結果が変わるロジックに対するテストはパターンを網羅しようとすると大変です。ロジックが変更されたときのテスト修正は骨が折れますし、ミスも多くなってしまいます。

そんな悩みを抱えていたとき、PHPテストフレームワーク「PEST」のCombining datasetsという機能を発見しました。この機能を使えば組み合わせパターンの管理が楽になるのではないかと思ったので試してみました!

目次

  1. Combining datasetsの紹介
  2. テスト題材
  3. PESTでテストコードを書いてみる
  4. 感想

Combing datasetsの紹介

Combing datasetsとはPHPテストフレームワーク「PEST」に搭載されている機能で、2つ以上のデータセットの総当たり組み合わせを生成してくれます。

https://pestphp.com/docs/datasets#combining-datasets

テスト題材

ユニットテストを行う対象のコードについて説明します。

今回は「注文した曜日」と「配送する地域」によって「翌日に配送できるかどうか」を判定する処理を想定してみます。

翌日配送可能な注文曜日: 平日(月〜金曜日)

翌日配送可能な配送地域:福岡、佐賀、大分、熊本

注文した曜日と配送地域が両方とも対象であれば翌日配送可能とし、それ以外の場合は不可となります。

コードはこんな感じです。canDeliveryTomorrow()メソッドをテストします。

<?php

namespace App\Models;

class OrderCondition
{
    private $deliveryArea;
    private $orderDay;
    private $canDeliveryTomorrowArea;
    private $canDeliveryTomorrowDay;

    public function __construct($deliveryArea, $orderDay)
    {
        $this->deliveryArea = $deliveryArea;
        $this->canDeliveryTomorrowArea = collect([
            'Fukuoka',
            'Saga',
            'Oita',
            'Kumamoto',
        ]);

        $this->orderDay = $orderDay;
        $this->canDeliveryTomorrowDay = collect([
            'Monday',
            'Tuesday',
            'Wednesday',
            'Thursday',
            'Friday'
        ]);
    }

    public function canDeliveryTomorrow(){
        return $this->isDeliveryTomorrowArea()
            && $this->isDeliveryTomorrowDay();
    }

    private function isDeliveryTomorrowArea(): bool
    {
        return $this->canDeliveryTomorrowArea->contains($this->deliveryArea);
    }

    private function isDeliveryTomorrowDay(): bool
    {
        return $this->canDeliveryTomorrowDay->contains($this->orderDay);
    }
}

PESTでテストコードを書いてみる

<?php
use App\Models\OrderCondition;

test('配送可能な曜日&&配送可能なエリア_翌日配送できる', function ($area, $day_of_week) {
    $orderCondition = new OrderCondition($area, $day_of_week);
    expect($orderCondition->canDeliveryTomorrow())->toBe(true);
})
    ->with('area_can_delivery_tomorrow')
    ->with('days_of_week_can_delivery_tomorrow');

test('配送可能な曜日&&配送不可能なエリア_翌日配送できない', function ($day_of_week, $area) {
    $orderCondition = new OrderCondition($area, $day_of_week);
    expect($orderCondition->canDeliveryTomorrow())->toBe(false);
})
    ->with('days_of_week_can_delivery_tomorrow')
    ->with('area_can_not_delivery_tomorrow');

test('配送不可能な曜日&&配送可能なエリア_翌日配送できない', function ($day_of_week, $area) {
    $orderCondition = new OrderCondition($area, $day_of_week);
    expect($orderCondition->canDeliveryTomorrow())->toBe(false);
})
    ->with('days_of_week_can_not_delivery_tomorrow')
    ->with('area_can_delivery_tomorrow');

test('配送不可能な曜日&&配送不可能なエリア_翌日配送できない', function ($day_of_week, $area) {
    $orderCondition = new OrderCondition($area, $day_of_week);
    expect($orderCondition->canDeliveryTomorrow())->toBe(false);
})
    ->with('days_of_week_can_not_delivery_tomorrow')
    ->with('area_can_not_delivery_tomorrow');

// 翌日配送可能な曜日
dataset('days_of_week_can_delivery_tomorrow', [
        'Monday',
        'Tuesday',
        'Wednesday',
        'Thursday',
        'Friday',
    ]
);

// 翌日配送不可能な曜日
dataset('days_of_week_can_not_delivery_tomorrow', [
        'Saturday',
        'Sunday',
    ]
);

// 翌日配送可能なエリア
dataset('area_can_delivery_tomorrow', [
        'Fukuoka',
        'Saga',
        'Oita',
        'Kumamoto',
    ]
);

// 翌日配送不可能なエリア
dataset('area_can_not_delivery_tomorrow', [
        'Nagasaki',
        'Miyazaki',
        'Kagoshima',
        'Okinawad',
    ]
);

ポイントは2点です!

  • dataset()でデータのまとまりを表現する
  • with()を使って組み合わせを表現する(combining dataset)

PESTではShared Datasetsという機能があり、dataset() という構文でまとまりがあるテストデータを名前付きで定義することができます。

下記の部分で翌日配送可能/不可能な曜日とエリアをdataset()で表現しています。

// 翌日配送可能な曜日
dataset('days_of_week_can_delivery_tomorrow', [
        'Monday',
        'Tuesday',
        'Wednesday',
        'Thursday',
        'Friday',
    ]
);

// 翌日配送不可能な曜日
dataset('days_of_week_can_not_delivery_tomorrow', [
        'Saturday',
        'Sunday',
    ]
);

// 翌日配送可能なエリア
dataset('area_can_delivery_tomorrow', [
        'Fukuoka',
        'Saga',
        'Oita',
        'Kumamoto',
    ]
);

// 翌日配送不可能なエリア
dataset('area_can_not_delivery_tomorrow', [
        'Nagasaki',
        'Miyazaki',
        'Kagoshima',
        'Okinawad',
    ]
);

また、8~9行目の部分で定義したデータセットをwith()を2回使って組み合わせることで自動的に総当たり組み合わせのデータセットを生成してくれます。

test('配送可能な曜日&&配送可能なエリア_翌日配送できる', function ($area, $day_of_week) {
    $orderCondition = new OrderCondition($area, $day_of_week);
    expect($orderCondition->canDeliveryTomorrow())->toBe(true);
})
    ->with('area_can_delivery_tomorrow')
    ->with('days_of_week_can_delivery_tomorrow');

下記は「曜日、エリアともに翌日配送不可な場合」ですが、総当たりでテストされていることがわかります。

テスト失敗時の表示もわかりやすいと思いました。

どの組み合わせの時に失敗したのか一目でわかります。

感想

今回はPESTのCombining datasetsを使った組み合わせテストを実装してみました!

PESTのデータセットを使うとまとまりがあるデータを名前付きで定義できるため可読性が向上し、Combining datasetsを使うことで組み合わせの総当たりを自動で生成してくれるのでテストコードの本質的な部分に集中できると思いました。

組み合わせに変更が生じた場合もデータセットの部分のみを変更すればよく、テストコードの修正が不要なのでメンテナンス性も上がると思われます。

PESTはLaravelとの互換性もあり、将来性を感じるテストフレームワークだと感じています。

今後もPESTの技術調査を進め、快適なテストライフを送れるよう努力していこうと思います!

takematsu

takematsu

Company : Fusic CO., LTD. Program Language : PHP, Java/Kotlin, Python Skills : AWS, Terraform, gRPC Interest: Serverless, TDD, Agile Practice