読者です 読者をやめる 読者になる 読者になる

「The Art of Unit Testing」を写経した時の、xUnit.netまわりのメモ

C# テスト xUnit.net

以前、「The Art of Unit Testing, Second Edition with examples in C# 」をxUnit.netで写経していた時に悩んだところなどを残しておきます。

 

環境

  • xUnit.net 2.0.0
  • VisualStudio 2013 Community

 

準備など

xUnit.net Runnerのインストール

「Instant Nancy」を写経した時以降にxUnit.netのバージョンが上がり、2.0.0になりました。

その時は--preが必要でしたが、現在では正式バージョンになっているため、不要になっています。

本体やRunnerのインストールは、公式ページの「Getting Started with xUnit.net」を確認します。
Getting Started with xUnit.net > xUnit.net

 

プロジェクトを作るところからの流れ

忘れた時のためのメモです。

  1. テスト対象のプロジェクトを作る (Hoge)
  2. テストプロジェクトをクラスライブラリとかで作る (Hoge.Test)
  3. テストプロジェクトにて、NuGetで xUnit.netをインストールする
  4. テストプロジェクト(Hoge.Test)にて、テスト対象プロジェクト(Hoge)を参照に追加する
  5. テストクラスにて、using Xunit;を使用の上、テストメソッド[Fact]を付ける
  6. あとはテストを書いていく

 

すべてのテストを実行するショートカット

xUnit.netとはあまり関係ないですが、デフォルトでは、 Ctrl + R & Ctrl + A です。

 

NUnitとの比較

xUnit.netのドキュメントに比較表があります。
xUnit.net - Unit testing framework for C# and .NET (a successor to NUnit) - Home

最終更新日が「Nov 14, 2012」となっているため、v2で廃止された[PropertyData]などの古い情報が残っている場合もありますが、大きくは変わっていないので参考になります。

比較表の中でも写経をしていて気になったものを、以下に残しておきます。

 

テストにパラメータを渡す場合

[Theory] + [xxxData]を使います。写経ではこんな感じです。

[Theory]
[InlineData("filewithgoodextension.SLF")]
[InlineData("filewithgoodextension.slf")]
public void IsValidLogFileName_ValidExtensions_ReturnsTrue(string file)
{
    bool result = analyzer.IsValidLogFileName(file);

    Assert.True(result);
}

 

例外のテスト

NUnitでは[ExpectedException]Assert.Catch()となりますが、xUnit.netでは、[Fact] + Assert.Throws() を使います。写経ではこんな感じです。

[Fact]
public void IsValidFileName_EmptyFileName_ThrowsException()
{
    var exception = Assert.Throws<ArgumentException>(
        () => analyzer.IsValidLogFileName(string.Empty));

    Assert.Equal("filename has to be provided", exception.Message);
}

Stackoverflowはこちら。
mstest - Unit test exception messages with xUnit - Stack Overflow

 
なお、v2にてAssert.DoesNotThrow()は削除されたようです。
Remove Assert.DoesNotThrow · Issue #188 · xunit/xunit

 

スキップするテスト

NUnit[Ignore]ですが、xUnit.netではFact(Skip = <reason>)となります。写経ではこんな感じです。

[Fact(Skip = "Use `IsValidLogFileName_ValidExtensions_ReturnsTrue` method")]
public void IsValidLogFileName_GoodExtensionLowercase_ReturnsTrue()
{
    bool result = analyzer.IsValidLogFileName("filewithgoodextension.slf");

    Assert.True(result);
}

 

カテゴリ分け

NUnit[Category]と同じようなものはなさそうで、代替として[Trait(<key>, <value>)]が挙げられていました。
Why I’m not migrating to xUnit completely « Trailmax Tech

写経ではこんな感じです。

[Fact]
[Trait("category", "fast test")]    // Categoryの代替案
public void IsValidFileName_BadExtension_ReturnsFalse()
{
    var localAnalyzer = new LogAnalyzer();
    bool result = localAnalyzer.IsValidLogFileName("filewithbadextension.foo");

    Assert.False(result);
}

 
なお、Traitを使うことで、VisualStudioのテストランナーのコンテキストメニューにてグループ化 > 特徴 を選ぶとカテゴリ的なもので分類されます。

   

xUnit.netのv1とv2で異なる部分

いくつかありますが、ここでも気になったものを残しておきます。

 

テスト結果を出力ウィンドウなどに出力する

v1のときはTrace.WriteLineDebug.WriteLineが使えたようですが、v2より変更となり使えなくなりました。
Capturing Output > xUnit.net

v2では、ITestOutputHelper型のオブジェクトをテストクラスのコンストラクタで受け取り、そのオブジェクトを使って出力します。写経ではこんな感じです。

public LogAnalyzerTests(Xunit.Abstractions.ITestOutputHelper output)
{
    this.output = output;
    output.WriteLine("Setup");

    // v2ではTraceやDebugは使えない
    System.Diagnostics.Trace.WriteLine("Trace Setup");
    System.Diagnostics.Debug.WriteLine("Debug Setup");
}

 
経緯などは以下のIssueにまとまっていました。

 

[PropertyData]の代わりの[MemberData]

xUnit.netのv2より[PropertyData]が廃止されて[MemberData]へとリネームされています。
Upgrading xunit.extensions > xUnit.net

MemberDataでは、staticなプロパティの他、staticメソッドやstaticフィールドが使えるようになりました。写経ではこんな感じです。

// xUnit.net v2から、PropertyDataがMemberDataへと変更
// 静的プロパティのほか、静的メソッドや静的メンバ変数も利用可能
[Theory]
[MemberData("StaticPropertyTestData")]
[MemberData("StaticMethodTestData")]
public void IsValidLogFileName_ValidExtensions_ChecksThem(string file, bool expected)
{
    bool result = analyzer.IsValidLogFileName(file);

    Assert.Equal(expected, result);
}



// MemberData用静的プロパティ
public static IEnumerable<object> StaticPropertyTestData
{
    get
    {
        return new[] {
            new object[] { "filewithgoodextension_property.SLF", true },
            new object[] { "filewithgoodextension_property.slf", true },
            new object[] { "filewithgoodextension_property.foo", false },
        };
    }
}

// MemberData用静的メンバ
public static IEnumerable<object> StaticMethodTestData()
{
    return new[] {
            new object[] { "filewithgoodextension_method.SLF", true },
            new object[] { "filewithgoodextension_method.slf", true },
            new object[] { "filewithgoodextension_method.foo", false },
        };
}

 

日本語情報

以下がまとまっていて、参考になりました。