tech.kayac.com

技術部新卒研修担当の fujiwara です。

前回の記事「2013年の新卒研修と社内ISUCONやりました - (1) 研修編」に引き続き、新卒研修の最後を飾るイベント、社内ISUCONについて詳しく振り返ります。

社内ISUCONとは

レギュレーションはこちらです。

  • 各チーム1台ずつ使用できる仮想マシン上で、お題のアプリケーションを動作させる
  • 外部からベンチマークを行って処理できたリクエスト数をスコアとする
  • アプリケーション、OS、ミドルウェアなど、どのようなチューニングを行ってもよい
  • ベンチマークスクリプトはデータの整合性をチェックするロジックが組み込まれており、アプリケーションとして不整合を起こしていることを検出するとFAIL(スコアなし)
  • 10:00〜17:00 までの作業中には適宜ベンチマークを実行できる
  • 作業終了後の最終計測でのスコアが高いものが優勝 (FAILしたら失格。17:00以前までのスコアは考慮しない)

参加したのは2013新卒8名が2名ずつで4チーム、先輩は10名を5チームに編成し、合計9チームです。

お題のアプリケーションはいわゆる nopaste (gistのようなもの) です。ユーザ登録、ログイン機能を備えており、投稿と★を付ける機能はログインした状態でないと使えません。サイドバーには最新投稿が100件まで並んでいます。

nopaste.png

ソースコードは一式公開しています

ログイン機能があるため、セッション管理が必要になるところが過去2回の本家 ISUCON とは異なるところでしょうか。

新卒チームは昨日まで実際に作成していたアプリケーションをベースに作業できる上、実行するサーバ環境も研修中の実習で使用していたもののため、先輩チームよりは内部の把握に有利です。これはハンデですね。

死闘の始まり

isucon-opening.jpg

10:00 から本家ばりのオープニングムービーに引き続きレギュレーション説明が行われ、各チームは作業を開始します。

1チームのみ、運営の不手際で仮想マシンに30分ほどログインできない事案がありましたが、これは優勝候補の一角「チーム kameda (handlename, kenjiskywalker)」だったのでハンデということでひとつ勘弁していただきたく…

ベンチマークは CIツール Ukigumo (に少し手を入れたもの) で実行し、スコアを一覧して表示できるようにしていました。

isucon-ukigumo.png

新卒の一撃

競技開始から2時間ほど経過した12時過ぎ、新卒チーム ganbaru (MacoTasu, ichinose) がここまでの最高スコア 13,000 over を叩き出し、先輩チームに動揺が走ります。初期スコアは200程度ですので、この時点で60倍以上の高速化をしたことになります。

社内isuconを終えて - Maco_Tasuの技術ブログによると、この時点でサイドバーで大量に飛ぶクエリの書き換えとindex追加、nginxで静的ファイルをさばくなどのチューニングが行われていたようです。

デッドヒート

なかなかどのチームもブレイクスルーを起こせないまま、しかし着実に先輩チームはスコアを伸ばしていきます。

チーム beer (soh335, Konboi) が 15時前に 20,000 に到達、同じく先輩のチーム kameda と fugu (macopy, 9re) がそれを追いかけます。

しかし午前中に会場の空気を震撼させた ganbaru が 16:25 に 23,000 超えでトップを奪還、激しいデッドヒートを繰り広げる展開となりました。

部長、動く

終了まであと1時間を切る激闘のさなか、2年目のtkuchikiとコンビを組み、あえて自分はコードを書かずにサポートに徹していた部長 syoji がここで満を持して動きます。

isucon-irc.png

しかし詰んだ。

終演

17:00 の競技終了直前、どのチームも作業をしてはベンチマークを繰り返し、計測ホストでは最大4並列でベンチマークが実行され、5分間の平均トラフィックは500Mbpsを超えました。

ここまでトップを争っていたチーム beer は終了直前に FAIL を連発し、焦りを隠せません。対して新卒の ganbaru は最後 16:55 に 22,774 を記録して首位に立ち、そのまま計測終了。

これはまさかの新卒チームの勝利か。入社2週間の新卒に叩きのめされた先輩の威厳はどうなる。

最終計測

競技終了後、最終計測に入ります。

公平を期すため全マシンを一回再起動。運営側がDBを初期データにリセットし、その後各チームが初期化の手作業を行うことはできますが、ベンチマークは実行できません。

そして、競技中のスコアは、最終計測には全く加味されません。

これまでどれだけ FAIL していても、最終計測で成功してスコアを残せばそれが採用。ここまでどれだけ中間スコアが高くても、最終計測で FAIL したら失格、スコアなし。それが ISUCON です。

計測中には、運営側からどんなチューニングをやった?というアンケートなどを行っていました。

結果発表

優勝 team: beer (SCORE 25,133.4)

終了直前にFAIL連発して危ぶまれていた beer でしたが、最終計測を見事にpassして優勝です!

一方、勝利を8割方手中にしたと思われた ganbaru はFAILで失格。2013新卒チームは残念ながら全チームが FAIL でスコアなしという結果に終わりました。

あとから聞くところによると ganbaru はデータリセット後の初期化処理をミスしたそうですが、本番でミスをしないのも仕事をする上での実力のうちですね。

最終結果は以下のようになりました。先輩チームは最終的にはきっちりスコアを出して上位に食い込むあたり、さすがに実戦で鍛えられた力を感じます。

   team     | status  |  score     | members
------------|---------|------------|----------------------------------
beer        | SUCCESS | 25,133.4   | soh335 Konboi
kameda      | SUCCESS | 24,125.33  | handlename kenjiskywalker
fugu        | SUCCESS | 21,768.46  | macopy 9re
syoji       | SUCCESS |  7,623.68  | tkuchiki syoji
ganbaru *   | FAIL    | 19,914.95  | MacoTasu ichinose
ky          | FAIL    |  9,078.29  | tei-you chen-xiao
p_shake *   | FAIL    |  8,806.17  | keishake p_chin
chinatown * | FAIL    |    687.39  | m0t0k1ch1 piyon
gumma *     | FAIL    |    616.7   | gs hosono
* は2013新卒チーム

以下参考記録
------------|---------|------------|----------------------------------
reference   | SUCCESS | 53,977.37  |
acidlemon   | SUCCESS | 24,235.15  |

講評

今回の社内 ISUCON の出題では、最初に提供されるリファレンス実装には意図的にかなり無駄な処理が含まれています。研修の一環ということもあり、特に罠らしい罠は仕掛けていません。初期スコアは200 (1分間のHTTPリクエスト数) 程度ですが、

  • DBに適切にindex追加
  • サイドバーで100回のループ中に都度発行されるクエリを JOIN に書き換える

だけで 13,000 程度のスコアに到達できるような出題になっています。 つまり、ここまで到達できればほぼ合格点、という水準がスコア 13,000 以上ということになります。

優勝争いはそのあたりを確実にクリアした上で、サイドバーのレンダリングが重いのを部分的に cache するなどの方策を適切に入れることができるか、というのが焦点になったようです。

優勝チームのblogエントリは以下です。おめでとうございます!

(おまけ) 優勝チームの2倍のスコアを出す方法

出題者(fujiwara)はこのアプリを火曜日の夜中に書き、水曜にテストを書き、木曜にベンチマーククライアントを書いて金曜に本戦を行った関係で、どこまでスコアを伸ばせるかを事前に追い込むことができていませんでした。

ベンチマーククライアント側の限界を確認するために、nginx で return 200 するだけのサーバに対して負荷を掛けて、最高100,000程度まで到達できることを確認したのみです。

ということで、競技中に自分もリファレンス実装からどこまでスコアを伸ばせるかチャレンジしていました。

結果、優勝チームの約2倍、53,000のスコアを記録することができましたが、これはベンチマーク計測でどのような整合性チェックをしているか(=どのようにすればチェックをかいくぐれるか) を把握していたからできた手法という感があります。

チェッカーはログインした上で投稿や★をPOSTし、その結果がサイドバーなどに1秒後に反映されているかどうか見てくるのですが、ログインしていないで単に負荷を掛けるだけのクライアントではそのチェックが緩い、という特性を利用します。

具体的には、varnish をフロントに置き、以下のような設定をしました。

  • GET 以外は application に pipe (キャッシュしないでそのまま返す)
  • Cookieに plack_session=[a-f0-9] が含まれている場合 (ログインしてセッションが維持されている) は application に pipe
  • それ以外は静的ファイルまたは application へ lookup (キャッシュする)

しかし、キャッシュ可能な画面でもアプリケーションは Cookie を発行してそれをベンチマーククライアントが維持してしまい、結果 Cookie の有無だけではログイン状態を判別できないという(出題意図にない) 実装が競技中に判明してしまったため、急遽必要ない Set-Cookie ヘッダを削除して Cache 可能にする Plack::Middleware を書くことになりました。

スコアが出せたのは終了13分前。出題者としては優勝チームのスコアに負けるわけにはいかず、相当ギリギリの戦いでした…

ベンチマーククライアントと動かしかたについても公開しています ので参考まで。

まとめ

新卒研修の最後に2013新卒 vs 2012新卒でバトルしたらおもしろいんじゃない?というぐらいのネタで開催することになった社内 ISUCON ですが、参加者の誰に聞いても楽しい(ただしFAILしたチームは悔しい) という、非常に充実したイベントになりました。

普段の仕事ではいろんなしがらみがあって同一条件で競うことはなかなかできないわけですが、そういう事情を一切排除して、公平な条件でガチでぶつかり合うというのはとても楽しいことですね。

みなさんも是非開催してみると想像以上に面白いのでは、と思います。

# ただし、出題者はかなり胃が痛い思いをする可能性がありますのでお気を付けください

4月になり、新人が入ってくる季節になりました。技術部新卒研修担当の fujiwara です。

これまで弊社技術部では特に技術部としての研修というものを行っていなかったのですが、今年は何かやりたいねということで人事部に7日間確保してもらい、主に acidlemon と2人で新卒8人に研修をすることになりました。

研修の資料などは GitHub でプライベートリポジトリを作って作業しており、それをまとめたものを GitHub上で kayac/newbie-training として公開しています。

どのような内容にするかは事前に昨年度の新卒を含めたエンジニア陣で(社内勉強会でビールを飲みながら)話し合い、

  • 現場で実務に追われてしまうと身につきにくい基礎的な内容
  • コードを書ける人は多いけどLinuxサーバの操作は不慣れな人が多いので、そのあたりを底上げする内容

をメインに構成してみました。また、7日目の最終日には社内 ISUCON (Webアプリケーション高速化コンテスト) を行い、昨年度の新卒を含む先輩エンジニアとガチで戦うイベントも用意しました。


詳細はリポジトリの方を見ていただくとして、ざっと内容を説明します。

1日目 環境構築・インストール作業

手元のマシンに今後使うであろうソフトウェアの環境を整備してもらいます。Macの人は homebrew などを利用して直接、Windowsの人は VirtualBox で Linux VM を立ててそこにインストールという形を取りました。

社内で boxenのようなものがあればいいのですが、ないので手動で設定してもらいましたがこれで時間を使うのももったいないので何とかしたいところですね。

また、研修中の日報は各自、リポジトリに適当なフォーマット(markdownの人が多数)でcommitしてもらうようにしました。

社内にIRCチャンネルも用意し、GitHubにコミットがあった場合に IRC hook で通知するようにも設定してあります。

2日目 severspecとChefによるサーバ構築実習

1人に1台ずつKVMの仮想マシンを配布し、そのサーバをセットアップする実習です。 最近はサーバの構成管理に Chef を利用しているため、慣れてもらうのも目的です。

serverspec を利用し、事前に作成したテストを通るようにセットアップを行ってもらいます。サーバ構築もテストする時代ですね。

  • まず serverspecが通る状態へ手動でセットアップ (メモを取る)
  • 通ったらVMをrollbackして初期状態へ戻す
  • 今度はChefでレシピを書いてserverspecを通す
  • それが通ったらもう一度 rollback
  • まっさらな状態から Chef recipe を適用し、テストを通るかどうか確認

3日目 Perl

自社サービスのサーバサイドアプリケーションは Perl がメインのため、最低限 Perl の読み書きができてもらいたいということで Perl に1日あてました。

テキストには、はてなさんが公開されている Perl によるオブジェクト指向プログラミング を利用させていただきました。大変充実したテキストでありがたかったです。

4日目 HTTP・WebApp

Webに携わるエンジニアとして、HTTPはどのようなものなのかを理解することは大変重要です。 ブラウザやWebサーバを単に扱っているだけでは把握しづらい HTTP の生の通信を扱ってもらいます。

  • 各自構築したサーバ上で ngrep を使用してパケットキャプチャ
    • HTTPで何が流れているのかを見る
  • telnet で手で HTTP を喋って HTTP client になる
  • nc -l で手で応答を返す HTTP サーバになる
  • PSGI を生で使った Web Application を作る
  • それを Amon2::Lite に移植してフレームワークの威力を実感する

5日目 RDBMS・DBを使ったWebApp

データストアとしては最近はいわゆる NoSQL も多いとはいえ、基本的には MySQL を利用しているのでそれの基礎の基礎です。

まず mysql コンソールで操作し、次に Perl + DBI で MySQL を使う演習を行いました。 ここまではセキュリティについて基本的には触れていませんでしたが、外部からの入力を不適切に扱うことでSQL injectionという脆弱性に繋がる話もします。

また、昨日作ってもらった PSGI を直接使う WebApp のコードを見ながら講評をし、実は全員が HTML escape を行わないことによる XSS を作ってしまっていたことにも触れました。

その後、最終日の ISUCON に向けての課題アプリケーション(ログイン機能付きnopaste) の雛形に、実装とテストを追加して仕上げてもらう課題に移ります。

6日目 フレームワークを使ったWebApplicationの作成とテスト

前日に引き続き、Amon2 を使用した nopaste アプリの実装とテスト。

明日の ISUCON にむけて、現在本番で動いているサービスはどのようなサーバ構成になっているのかの講義も行いました。参考 : カヤック流ソーシャルアプリの作り方 インフラ編

7日目 社内ISUCON

いよいよ最終日。

新卒8人を2人ずつで4チーム、先輩エンジニアは技術部長から2012新卒まで10人参加したのでこれも2人ずつで5チーム、計9チームを編成し、昨日までに作成した nopaste アプリケーションを題材にどれだけリクエストを捌けるかのベンチマークで勝敗を競う『社内ISUCON』を開催しました。

ちなみに本家 ISUCON についてはこちらのblogなどをご覧ください

……というところで、次のエントリ「2013年の新卒研修と社内ISUCONやりました - (2) ISUCON死闘編」へ続きます!

みなさんこんばんは。今年の2月に入社してWeb業界というものがよくわからないままiPhoneアプリ開発やらnode.jsでサーバ開発やらPerlでサーバ開発やらC#でUnity開発やらをやっていたら年が暮れかかっていた@acidlemonです。

もともと私はC++が専門で、SIerの業界にいたのでしかたなくJavaもやっていたという感じだったのですが、Web業界に転職してみたらC++でプログラムを書く人がほぼいなかったということで、今年だけで新規に4言語も手をつけていて環境の激変っぷりに自分でも驚いています。

さて、24日間にわたってお送りしてまいりましたtech.kayac.com Advent Calendar 2012も今日が最終日。2日目にしていきなりJavaScriptでおっぱいが動き始めた時はどうなることかと思いました。しかし、振り返ってみるとDDLをGitで管理したり、Procletで開発中のミドルウェアの立ち上げを超簡単にしたりといった開発を効率よく進めるためのイノベーションや、5年前から続いているWebサービスを時間と技術革新の流れに合わせていったり、ユーザの行動ログを可視化してカスタマーサポートを効率化したりといった、運用を効率よく進めるためのイノベーションなのがありましたね! いやーすごい。

昨日のHokkaido.pmのため22〜24日は北海道に行っていたため遅めのアップロードとなりましたが、最終日のアドベントカレンダーいってみましょうー(よくわからないテンションになっています)。

本題

最初にも書きましたが最近はPerlでサーバを書きつつC#でUnityクライアントの通信部分を書くという二足のわらじ(?)のような生活をしております。

で、Perlは単体テストしやすい言語なので大変感動しているのですが、C#/Unityとなるとなかなか苦痛な感じです。私のプロジェクトにはフロント専門のUnityチームがいるので、私は通信部分とモデル部分のみ書いています。つまり表示部分を全く書かずにロジックだけのコードを書いているため、単体テストを書くのはやはり必須。とりあえず世間的にはUnityの単体テストするならSharpUnitだよねみたいなノリになっているようなのですが、通信部分をテストするにはこれもなかなかつらいものがありました。

というのも、SharpUnitは非同期実行(コルーチン実行)をサポートしておらず、同期実行できるメソッドしかテストできないんです。まぁサーバから来たJSONをパースしてクラスオブジェクトを組み立てるところは同期実行なので別によいんですが、通信が絡む部分はUnityEngine.WWWを使う必要があるためどうしてもコルーチンを実行してyield returnでゆっくり待つ必要が出てきます。

「通信部分なんて単体テストの範疇じゃないじゃん、サーバ側のテストでやれよ」というご意見もごもっともなのですが、だからといってサーバに接続出来ないときのエラー等をテストしないわけにも行かないので、そういうところはクライアント側でちゃんとテストする必要があります。それを自動化したかったわけですね!

SharpUnitを改造したよ

ということで、今回はSharpUnitを改造してコルーチンのテストもできるようにしました。

オリジナルのソースがgithubに上がっているようなのでとりあえずforkして改造したものをgithubにあげています

大枠の使い方(Unity上での動かしかた)についてはあんまり変わっておりません。以下の記事が大変参考になります。というか私がまず最初に動かして動作チェックをしたときにこれを参考にさせていただきました。ありがとうございます。

とりあえずUnity側で必要な作業は、テスト時に動かすシーンに対してTestRunnerというGameObjectを追加して、そこにUnity3D_TestRunner.csUnity3D_TestSuite.csをアタッチする感じになります。

2012_24_img.png

さてオリジナルのSharpUnitとの差分ですが、具体的には以下のような点を改造しています。

  • 従来のTestCaseの使用感は変更せず、それとは別にIEnumeratorを戻り値とする関数をテストできるUnityTestCaseクラスを追加
  • TestCaseクラスからITestCaseインタフェースを分離
  • TestSuiteクラスを使うのはやめて、Unity3D_TestSuiteクラスを追加。ITestCaseを実装しているTestCaseクラスとUnityTestCaseクラスの両方に対応(ファイルはそのまま残しています)

改造理由についてちょっと触れておくと、TestCaseクラスはコルーチン実行できないテストフレームワークなので、これを元にコルーチンを実行できるUnityTestCaseクラスを作りました。Suite側でTestCaseとUnityTestCaseの両方を同じように扱えるようにするため、UnityTestCase側のインタフェースをITestCaseに分離し、TestCaseをそれに適応するようにTestResultを受け取る部分のインタフェースを変更しています。

これでTestSuiteでTestCaseとUnityTestCaseを同じように扱えるようになったのですが、そもそもTestSuiteからテストケースのコルーチンを実行するにはTestSuite自体がMonoBehaviorを継承してGameObjectとしてシーンにアタッチできるようになっている必要があるため、TestSuiteをベースにUnity3D_TestSuiteクラスを作ってGameObjectとして利用できるようにしています。

C++脳の私はこの辺の再実装は多重継承使ってやればいいじゃーんと思ってたんですが、C#には多重継承の仕組みがなかったのでてっとり速くコピペですませてしまっています。コレ書いててmix-in的なことをやればよかったんじゃねって今思いました。

どんな感じでつかうの?

そもそもSharpUnitを使ったことがある人はこの記事読んでも「へー」くらいで終わるかと思うんですが、Unityで単体テストとかやんねーだろ的な人もいるかと思いますので、具体的な例をこちらに書きます。

たとえば、こんなクラスがあったとしましょう。

class Plan {
    private string _Title;
    private string _Text;
    public string Title { get { return _Title; } }
    public string Text { get { return _Text; } }

    // コンストラクタ
    public Plan(string title, string text) {
        _Title = title;
        _Text = text;
    }
}

このオブジェクトをテストするとなるとこんなコードを書きます。

using SharpUnit;

public class PlanTestCase : TestCase {
    public override void SetUp() {
    }

    public override void TearDown() {
    }

    [UnitTest]
    public void TestConstructor() {
        // ふつうに作る
        Plan val1 = new Plan("Christmas", "Let's play tennis!");

        Assert.Equal(val1.Title, "Christmas", "title ok");
        Assert.Equal(val1.Text, "Let's play tennis!", "text ok");

        // 本文がぬるぽ
        Plan val2 = new Plan("Christmas", null);
        Assert.Equal(val2.Title, "Christmas", "title ok");
        Assert.Null(val2.Text, "text is null ok");
    }
}

ここまでは既存のSharpUnitで可能でした。ここからが問題で、かなり誰得感はありますがPlanをインターネットから取ってきて作るファクトリクラスがあったとしましょう。まーUnityでやる必要性はないですがサンプルなのでそんなもんです。

using UnityEngine;

class InternetPlanFactory {
    private Plan _Plan;
    public Plan Plan { get { return _Plan; } }

    // 特定のURLにアクセスし、1行目をタイトル、2行目をテキストとしてオブジェクトを生成
    public IEnumerator PlanFromUrl(string url) {
        WWW www = new WWW(url);
        yield return www;

        if (www.error) {
            // ノープランだ
            _Plan = null;
        } else {
            // 適当にPlanつくる
            string[] lines = www.text.Split('\n');
            string title = lines[0];
            string text = lines.Length > 1 ? lines[1] : "";

            _Plan = new Plan(title, text);
        }
    }
}

このファクトリクラスをテストするならまぁ大体この2ケースでしょうか(ホントは1行しかなかったときのテストもしたいですね)。

  • ふつうにうまくいった
  • 接続出来なかったり404とかのエラーが返ってきたりした

が、このファクトリクラスのメソッドはコルーチン実行して結果が帰ってくるのを待たないとちゃんとテストできません。そこで、今回使ったUnityTestCaseを使います。

using SharpUnit;

public class InternetPlanFactoryTestCase : UnityTestCase {
    private InternetPlanFactory factory;
    public override void SetUp() {
        factory = new InternetPlanFactory();
    }

    public override void TearDown() {
        factory = null;
    }

    [UnitTest]
    public IEnumerator TestPlanFromUrlOk() {
        // 普通にうまくいきそうなパターン
        Unity3D_TestSuite runner = GameObject.Find("TestRunner").GetComponent<Unity3D_TestSuite>();
        yield return runner.StartCoroutine(factory.PlanFromUrl("http://www.example.org/example.txt"));

        try {
            Assert.NotNull(factory.Plan, "plan is not null ok");
            Assert.NotNull(factory.Plan.Title, "plan has title ok");
            Assert.NotNull(factory.Plan.Text, "plan has text ok");
        } catch (TestException e) {
            MarkAsFailure(e);
            yield break;
        }

        DoneTesting();
    }

    [UnitTest]
    public IEnumerator TestPlanFromUrlError() {
        // ダメっぽそうなパターン
        Unity3D_TestSuite runner = GameObject.Find("TestRunner").GetComponent<Unity3D_TestSuite>();
        yield return runner.StartCoroutine(factory.PlanFromUrl("http://www.example.org/error.txt"));

        try {
            Assert.Null(factory.Plan, "plan is null ok");
        } catch (TestException e) {
            MarkAsFailure(e);
            yield break;
        }

        DoneTesting();
    }

}

さていくつか普通のTestCaseと違う点が出てきました。1つめはテスト関数の戻り値がIEnumeratorになってることですね。テストを走らせる呼び出し元が yield return StartCoroutine(testMethod); というような感じで呼び出しますのでこれに対応できるようにIEnumeratorにします。これによりテスト関数の中でyield returnを使えるようになるというわけですね。

2つめはAssertをtryで囲んでいることです。普通のTestCaseであればAssertでTestExceptionが飛んだら呼び出し元がcatchしてよしなにエラーレポートしてくれるところなんですが、C#にはtry-catchブロックの中にyield returnを書けないという制限があります。まぁ複雑になるしそれで正解だと思います。が、それによりテストを走らせる呼び出し元でcatchする作戦が使えませんので、テスト関数の中でちゃんとtry-catchして、catchしたらUnityTestCaseが持ってるMarkAsFailure関数でTestExceptionが発生したことをマークしておきます。呼び出し元はyield returnからコルーチンが帰ってきたらTestExceptionがセットされているかをチェックしています。

3つめはStartCoroutineの呼び出し方です。テストはコルーチンとして実行する必要があるのですが、コルーチンとして実行するにもロジックしかないメソッドのためGameObjectがありません。そこで、テストを走らせるためにシーンに追加したGameObjectからUnity3D_TestSuiteクラスのコンポーネントを取得して、こいつをコルーチンランナーとして活用しています。

あとDoneTesting()っていうのを呼んでいるのはPerlでテストを書いている人にはおなじみのdone_testingですね。IEnumeratorな関数である以上適当に途中にyield breakを書いてしまうとそこでテストを中断出来てしまい、テストが全部走らないままパスしたように見えてしまうことがありますので、それの対策にDoneTesting()を呼ばないとFailする機構をいれておいたというところです(まぁあってもなくてもいいとは思いますが、趣味で入れてしまったものです)。

まとめ

かけ足で紹介してきましたUnityでも使えるテストフレームワークSharpUnitの改造版、私はC#歴が浅いため結構これで満足しちゃっているのですが、もし「こうしたらもっと使いやすくなると思う!」などありましたらコメントいただければとおもいます。

それでは皆さん、よいクリスマス、そしてよい新年をお迎えください。カヤック技術部のアドベントカレンダーを購読いただきありがとうございました!