.NET Core(C#): xUnitのAssert使用方法

はじめに

  • 次の環境を使用して動作確認しています。
    OSWindows 10(64ビット)
    IDEMicrosoft Visual Studio Community 2019(16.8.5) + C#(8.0)
    パッケージMicrosoft.NET.Test.Sdk 16.10.0
    xunit 2.4.1
    xunit.runner.visualstudio 2.4.3
  • “Assert”や”Assertion”は一般的に主張・表明する旨の意味です。プログラミングにおいては「プログラムの前提条件を示す」ために使用される用語です。ここでは便宜上、「検証」と表現します。
  • 完全なソースコードはこちらで公開しています。
  • モックを使用する場合の例はこちらで紹介しています。
  • 参考

基本的な検証

Assertクラスで提供されるstaticメソッドを使用して検証を行えます。

  • 基本的な検証はAssert.Equal()で行います。
    第1引数には「期待値」、第2引数は「実行値」(テスト実行した結果)を指定します。

    Assert.Equal( 期待値, 実行値 );

    期待値と実行値は、検証失敗時のメッセージのExpected, Actualで表示されます。

  • 期待値がnullやtrue/false等の特定の値になる場合は、Assert.Null(), NotNull(), True(), False()等のメソッドを使用できます。
  • Assert.Equal()とAssert.Same()の違い
    Equal()は「深い比較」、Same()は「浅い比較」を行う違いがあります。

    Assert.Equal()オブジェクトのEquals()メソッドを実行して同一であることを判定(深い比較)します。
    Assert.Same()同一のインスタンス(変数の参照先が同一)かどうかを判定(浅い比較)します。
    この性質上、検証の対象は参照型に限られます。char, int等の値型は代入時に常に新しいインスタンス(コピー)が作成されるので、検証が常に失敗します。
  • Equals()というメソッドも提供されていますが、こちらは古いメソッドであり非推奨になっています。

型の検証

Assert.IsAssignableFrom()やAssert.IsType()を使って型を検証できます。

  • Assert.IsAssignableFrom()とAssert.IsType()の違い
    IsAssignableFrom()は抽象クラスやインターフェイスの継承・実装の関係性の判定、IsType()は実行時の型を判定します。

    Assert.IsAssignableFrom()任意のクラス・インターフェイスを継承・実装しているかを判定できます。
    仮パラメータとして抽象クラスやインターフェイスを指定できます。
    Assert.IsType()実行時の型(実際のクラス)を判定します。
    仮パラメータとして具象クラスを指定する必要があります。抽象クラスやインターフェイスも指定できますが、この場合は常に検証に失敗します。
  • どちらもメソッドも成功時に戻り値として「検証した型にキャストしたオブジェクト」を返却します。後続の検証を行いたい場合はそれを使用できます。

例外の検証

例外がスローされたかどうかはAssert.Throws()やAssert.ThrowsAsync()で検証できます。

  • これらのメソッドではスローされた例外型を検証できますが、例外内容の検証までできません。
  • 想定とは別の原因で同じ例外型がスローされる場合も考えられるので、上記例のようにEqual()やStartWith()等で例外メッセージを検証することをおすすめします。

コレクションの基本的な検証

Assert.Equal()やコレクション向けのAssert.Empty(), Assert.Contains()などのメソッド群では、IEnumerable型を指定できます。そのため、このインターフェイスを実装している配列リストディクショナリなどを検証できます。

コレクションの内容の検証

コレクションの内容は、Assert.Collection()やAssert.All()で検証できます。

  • コレクションに含まれる要素毎に異なる検証を行う場合は、Assert.Collection()を使用します。N個の要素を検証する場合、要素内容を検証するためのラムダ式をN個指定する必要があります。
  • コレクションに含まれる全ての要素に同じ検証を行う場合は、Assert.All()を使用します。要素数に関わらず、検証用ラムダ式を1つ指定します。

上記のサンプルで使用しているTestDataの定義は次の通りです。

独自クラスを格納するリストの検証

独自クラスを格納するリストをAssert.Equal()で検証すると失敗します。
Assert.Equal()で検証できるようにするためには、独自クラスでEquals()をオーバーロードするか、Assert.Equal()の第3引数として等価比較クラス(IEqualityComparerを実装したクラス)を指定する必要があります。

上記のサンプルで使用しているLData1, LData2の定義は次の通りです。LData1はEquals()をオーバーロードせず、LData2はオーバーロードしています。
Equals()やGetHashCode()の実装は、VisualStudio 2019のリファクタリング機能で簡単に追加できます。

サンプルで使用している等価比較クラスLData1Comparerの定義は次の通りです。
IEqualityComparerインターフェイスで要求されるEquals()とGetHashCode()を実装しています。
今回の用途ではGetHashCode()は使用しないので適当な実装にしています。詳細はObject.GetHashCode()のリファレンスをご覧ください。

独自クラスを格納するディクショナリの検証

前述のリストと同様、独自クラスを格納するディクショナリをAssert.Equal()で検証すると失敗します。
Assert.Equal()で検証できるようにするためには、独自クラスでEquals()をオーバーロードするか、Assert.Equal()の第3引数として等価比較クラスを指定する必要があります。

上記のサンプルで使用しているDData1, DData2の定義は次の通りです。DData1はEquals()をオーバーロードせず、DData2はオーバーロードしています。
Equals()やGetHashCode()は、VisualStudio 2019のリファクタリング機能で簡単に追加できます。

サンプルで使用している等価比較クラスDicComparerの定義は次の通りです。
前述の「独自クラスを格納するリストの検証」と同様に実装しているので、詳細はそちらをご覧ください。