Visual Studio 2017 で Live Unit Testing(xUnit)した感想

3月7日にVisual Studio 2017の正式リリースを控えた週末に、RC版でチョロチョロと遊んでいました。
その中で「Live Unit Testing」について書いてみたいと思います。
この機能は、結構わかりやすい機能で、すでに多くの方がブログ等で紹介されています。
MsTestベース の説明が多そうなので、大して変わりませんが xUnit を使った Live Unit Testing をここでは紹介します。

テスト対象のプロジェクトを作成

プロジェクトの作成

メニュー「ファイル → 新規作成 → プロジェクト」を選択。
テンプレートは「クラスライブラリ(.NET Framework)」、プロジェクト名は「HogeHogeLibrary」とします。

f:id:daigo-knowlbo:20170306004125p:plain
※余談ですが「.NET Core」とか「.NET Standard」とか「ポータブル」とか、きちんと学んでいないと何が何だかになってしまいそうですね・・・3/7版でもこのラインで行くのかな?(まあ、現在の.NETの状況的にはこれが正しい姿ですね)

テスト対象クラスの追加

「Class1.cs」は削除して、テスト対象のクラスを追加します。
ソリューションエクスプローラで HogeHogeLibrary をマウス右ボタンクリック。メニューの「追加 → クラス」を選択します。

f:id:daigo-knowlbo:20170306004647p:plain

「Programmer.cs」を追加します。

f:id:daigo-knowlbo:20170306005417p:plain

実装は以下の通り。

// HogeHogeLibrary\Programmer.cs
using System;

namespace HogeHogeLibrary
{
  public class Programmer
  {
    public int Stamina { get; set; } = 100;

    public void Work(int hour)
    {
      this.Stamina -= hour * 10;
    }

    public void HaveLunch()
    {
      this.Stamina += 30;
    }

    public void Sleep(int minutes)
    {
      this.Stamina += minutes * 5;
    }
  }
}

彼は
* スタミナがデフォルトで100あります。
* 1時間働くとスタミナが10減ります。
* ランチをとるとスタミナが30増えます。
* 1分居眠りするとスタミナが5増えます。

テストプロジェクトを作成

プロジェクトの作成

では Programerクラス をテストするプロジェクトを作成します。
ソリューションエクスプローラで、ソリューションをマウス右ボタンクリック。メニューの「追加 → 新しいプロジェクト」を選択します。

f:id:daigo-knowlbo:20170306010015p:plain

テンプレートは「クラスライブラリ(.NET Framework)」、プロジェクト名は「HogeHogeLibrary.Test」とします。

f:id:daigo-knowlbo:20170306010134p:plain

xunitをNuGetから追加

ソリューションエクスプローラからHogeHogeLibrary.Testをマウス右ボタンクリック。メニューの「NuGetパッケージの管理」を選択します。

f:id:daigo-knowlbo:20170306010329p:plain

「xunit」をインストールします。

f:id:daigo-knowlbo:20170306010600p:plain

「xunit.runner.visualstudio」をインストールします。

f:id:daigo-knowlbo:20170306010824p:plain

HogeHogeLibraryへの参照を追加

テスト対象プロジェクト「HogeHogeLibrary」への参照を追加します。
ソリューションエクスプローラで「HogeHogeLibrary.Test → 参照」をマウス右ボタンクリック。メニューの「参照の追加」を選択します。

f:id:daigo-knowlbo:20170306011121p:plain

f:id:daigo-knowlbo:20170306011115p:plain

テストクラスを追加

「Class1.cs」は削除して、テスト対象のクラスを追加します。
ソリューションエクスプローラで HogeHogeLibrary.Test をマウス右ボタンクリック。メニューの「追加 → クラス」を選択します。

f:id:daigo-knowlbo:20170306011332p:plain

「ProgrammerTest.cs」を追加します。

f:id:daigo-knowlbo:20170306011511p:plain

実装は以下の通り。

// HogeHogeLibrary.Test\ProgrammerTest.cs
using Xunit;

using HogeHogeLibrary;

namespace HogeHogeLibrary.Test
{
  public class ProgrammerTest
  {
    [Fact]
    public void HardWorkTest()
    {
      var programmer = new Programmer();
      programmer.Work(3);
      programmer.HaveLunch();
      programmer.Work(10);

      Assert.Equal<int>(0, programmer.Stamina);
    }

    [Fact]
    public void NormalWorkTest()
    {
      var programmer = new Programmer();
      programmer.Work(3);
      programmer.HaveLunch();
      programmer.Work(5);

      Assert.Equal<int>(50, programmer.Stamina);
    }
  }
}
  • HardWorkTest()は、プログラマをスタミナ0までめいっぱい働かせます!
  • NormalWorkTest()は、プログラマを健全に働かせます!

一度ビルドしておきましょう。

テストエクスプローラの表示

メニュー「テスト → ウィンドウ → テストエクスプローラ」を選択します。
「すべて実行」をクリックするとテストが実行され、無事、成功の「緑」を得られます。

f:id:daigo-knowlbo:20170306012436p:plain

Live Unit Testingを開始

メニュー「テスト → Live Unit Testing → 開始」を選択します。
すると、コードエディタ上に緑のチェックマークがつきます。

f:id:daigo-knowlbo:20170306012614p:plain

Liveする!

では、試しにProgrammerクラスのコードをいじってみます。
プログラマ達は、ジムに通いデフォルトスタミナを120に引き上げました。

// HogeHogeLibrary\Programmer.cs
public int Stamina { get; set; } = 120;

すると、ワンテンポの後、以下のように Programmer.cs / ProgrammerTest.cs / テストエクスプローラ の表示が赤くなります。

f:id:daigo-knowlbo:20170306013010p:plain

f:id:daigo-knowlbo:20170306013029p:plain

仕様が変わったProgrammerに合わせてテストコードを書き直してあげると、緑に戻ります。

f:id:daigo-knowlbo:20170306013308p:plain

どのタイミングで動くの?

なんか「カッコいー、未来的ー」って思いますが、どのタイミングで Live Unit Testing は動いているのでしょうか?
見ていると「連続したキー入力が終わって1秒くらい後」には、ビルドが走ってテストが実行されているみたいです。

まとめ(思ったこと)

ということで「Live Unit Testing」を有効にしていると、かなり頻繁に「ビルド→テスト」が繰り返し実行されます。
個人的には「ソースファイルを保存したタイミング」辺りでいいんじゃないかと思ってしまいましたが、製品版でも同様になるのでしょうか(どこかに設定とか有るのでしょうか・・・)。7日に改めて確認してみます。

そしてこのビルドは内部的なもののようで、プロジェクトのbinフォルダのアセンブリは更新されません。

また、Service / Domain / Repositoryなどのように、分離したプロジェクト構成の場合でもきちんと動いてくれます。つまり、Serviceに対するテストコードが、末端のRepositoryソースの変更に対しても反応して Live Unit Testing が行われます。

本投稿のようなシンプルなクラスのテストであれば問題ありませんが、データベースアクセス や 外部WebAPI呼び出し が行われるクラスなんかが絡んでいると、ガンガンアクセスが飛ぶことになると思います。
更新系の処理が予期せず多数走ったり、大き目のチーム開発だとルール化なんかも必要そうかなぁ・・・なんて思いました。

そして、思考が拡散して、そもそも ユニットテストの必要性・TDDの可否・DHH氏のTDD is dead・・・なんかの過去の記事を改めて読み返してしまいました。
個人的には、「細部に至るユニットテスト要らない」「ビジネス的粒度のユニットテスト+自動統合テスト」でおk派です。
(と言いながら、実プロジェクトでテストコードを書かないことが多いのはここだけの話・・・)