Perl Hackers Hub

第48回 Perlでの今風のゲームサーバ開発とテスト(3)

この記事を読むのに必要な時間:およそ 3.5 分

マスタデータをテストする

マスタデータで効果を切り替えできると,自由度が高いことの裏返しで,マスタデータに起因するバグが生まれやすくなります。ほかにも,JSONなどの形でスキーマレスなデータをカラムに格納することで,RDBMSRelational Database Management Systemの型に頼った値域などのチェックも弱くなります。

また,プランナーは必ずしもSQLに明るいわけではありません。どのカラムが何の意味を持っていて,どんな値が入るかは,設計者が説明しなければなりません。

そこで,ドキュメントとマスタデータの形式チェックを兼ねた,マスタデータのテストを記述することが考えられます。

マスタデータのカラムの値域をテストする例

MySQLに入ったitemsテーブルのeffect_typeの値域を,素朴にチェックする例を挙げます。

use Test::More;
subtest 'items.effect_typeは1から3までの値を持つ' => sub {
    my $rows = $dbh->selectrow_arrayref(
        'SELECT * FROM items',
        { Slice => {} },
    );
    for my $row (@$rows) {
        cmp_ok $row->{effect_type}, ">=", 1,
            'items.id='.$row->{id};
        cmp_ok $row->{effect_type}, "<=", 3,
            'items.id='.$row->{id};
    }
};
done_testing;

PostgreSQLなどではCHECK制約があるため,こういった定義もDDLに記述すればよいのですが,CHECK制約のないMySQLではテストでカバーします。

また,subtestの説明や,ここで言うcmp_okの説明部分も大事です。筆者のチームでは,このテストをプランナーがCIサーバで実行するため,何が原因でfailしたか理解できるような説明を,普段のテストよりも厚く書いています。

ただ,これではPerlのプログラムそのままで,Perlを読めなければ,failするまで何のテストをしているかを読み解くことができません。Perlプログラマー以外にもわかる,ドキュメントとしてのテストを目指すには難しすぎます。

カラムの値域のチェックを宣言的に書く

そこで,筆者が作成したモジュールであるTest::MasterData::Declareを用いて,宣言的にマスタデータのテストを記述します。

use Test::MasterData::Declare;
master_data {
    load_csv items => "master-data/csv/items.csv",

    table items => "effect_type",
        like_number 1 => 3;
};
done_testing;

ループなどを用いずに,カラムが満たすべき定義を書きます。これにより,一定の規則さえ覚えてしまえば,マスタデータが満たすべき条件を知ることができます。また,failしたときのための説明を書かなくても,なぜfailしたかを出力してくれます。

テストするレコードを条件指定してテストする

別の例として,effect_type=1のときは,effect_parameter.energyがあって,それが1から100までの値であるかを確認するテストを記述してみます。

master_data {
   load_csv items => "master-data/csv/items.csv",

    table items => "effect_parameter",
       if_column effect_type => 1,
       json energy =>
           like_number 1 => 100;
};
カラムの値が10刻みかどうかのテストをする

Perlプログラマー以外にも記述がわかりやすいことは,Perlプログラマー以外でもルールを追加できることにつながります。ゲームでは,マスタデータの値がプログラム上は正しくても,ゲームバランスが崩壊していたり,おもしろくない場合があります。そうなる条件は,パラメータと深く向き合っているプランナーのほうが理解しています。また,プランナーが自分でテスト上のルールを追加できれば,ほかの人にコードでルールを伝えられます。

簡単な例として,体力回復アイテムの回復量は10刻みでないとユーザーにわかりにくく,それ以外の値はミスなので禁止するケースを考えてみます。Test::MasterData::Declareを使用して書くと,

master_data {
   load_csv items => "master-data/csv/items.csv",

    table items => "effect_parameter",
        if_column effect_type => 1,
        json energy =>
            like_number 1 => 100,
            sub { $_[0] % 10 == 0 };
};

となります。

若干Perlコードが入りましたが,簡単なコードなので,プランナーでもコピー&ペーストで追加できるでしょう。

まとめ

ゲームサーバの運用と開発は,大量のアクセス,大量のデータ,大量のユーザーを扱いつつ,安定的に運用することが大事です。これは気合いだけでは実現できません。誰でも同じ結果になりやすく,引き継ぎやすく,理解しやすいしくみを整えていくことが必須です。

ゲーム以外のほかの種類のサービスにも,通じる部分があると思います。本稿を参考に,より良い開発のためのアイデアを,読者のみなさんが思いついて,サービスに適用していただけたらうれしく思います。

さて,次回の執筆者は水音ぴねさんで,テーマは「CPANモジュールの品質を支えるCI技術」です。

WEB+DB PRESS

本誌最新号をチェック!
WEB+DB PRESS Vol.113

2019年10月24日発売
B5判/160ページ
定価(本体1,480円+税)
ISBN978-4-297-10905-9

  • 特集1
    接続エラー,性能低下,権限エラー,クラウド障害
    AWSトラブル解決
    原因調査・対応・予防のノウハウ
  • 特集2
    Ruby書き方ドリル
    要点解説と例題で身に付く!
  • 特集3
    体験
    ドメイン駆動設計
    モデリングから実装までを一気に制覇
  • 一般記事
    FigmaによるUIデザイン
    デザイナーとエンジニアがオンラインで協業できる!
  • 一般記事
    入門
    SwooleによるPHP非同期処理
    高速化のための並列実行はどのように書くのか

著者プロフィール

谷脇真琴(たにわきまこと)

1989年生まれ。山口県出身。

面白法人カヤックのゲーム事業部でサーバサイドアプリケーションの開発とワークフローの整備に携わってきた。

趣味はゲームと3Dプリンタの製作。