MSTest アサーション

Assert 名前空間のMicrosoft.VisualStudio.TestTools.UnitTesting クラスを使用して特定の機能を検証します。 テスト メソッドは、アプリケーションでコードを実行しますが、 Assert ステートメントを含める場合にのみ正確性を報告します。

概要

MSTest には、次の 3 つのアサーション クラスが用意されています。

クラス 目的
Assert 値、型、および例外の汎用アサーション。
StringAssert パターン、部分文字列、および比較の文字列専用のアサーション。
CollectionAssert コレクションを比較および検証するためのコレクション アサーション。

Important

新しいコードの場合は、常に Assert クラスを使用します。 StringAssertクラスとCollectionAssert クラスは、今後のリリースで非推奨になる可能性があります。 これらは主に下位互換性のために維持されますが、アサーションを 3 つの型に分割すると検出可能性が低下するため、推奨されません。

すべてのアサーション メソッドは、アサーションが失敗したときに表示されるオプションのメッセージ パラメーターを受け取り、原因を特定するのに役立ちます。

Assert.AreEqual(expected, actual, "Values should match after processing");

Assert クラス

Assert クラスを使用して、テスト対象のコードが期待どおりに動作することを確認します。

MSTest 4.0 以降、すべての Assert API は引数式をキャプチャし、エラー メッセージに含めます。 このサポートにより、手動の message パラメーターなしで、より豊富な診断が提供されます。

一般的なアサーションメソッド

[TestMethod]
public async Task AssertExamples()
{
    // Equality
    Assert.AreEqual(5, calculator.Add(2, 3));
    Assert.AreNotEqual(0, result);

    // Reference equality
    Assert.AreSame(expected, actual);
    Assert.AreNotSame(obj1, obj2);

    // Boolean conditions
    Assert.IsTrue(result > 0);
    Assert.IsFalse(string.IsNullOrEmpty(name));

    // Null checks
    Assert.IsNull(optionalValue);
    Assert.IsNotNull(requiredValue);

    // Type checks
    Assert.IsInstanceOfType<IDisposable>(obj);
    Assert.IsNotInstanceOfType<string>(obj);

    // Exception testing (MSTest v3.8+)
    Assert.ThrowsExactly<ArgumentNullException>(() => service.Process(null!));
    await Assert.ThrowsExactlyAsync<InvalidOperationException>(
        async () => await service.ProcessAsync());
}

Assert.That メソッド

MSTest 4.0 以降では、 Assert.That はブール式を評価し、明確なエラー メッセージを生成します。 より高度な診断のために、 Assert.That[CallerArgumentExpression] を使用して式テキストを自動的にキャプチャします。

Assert.That(order.Total > 0);

使用可能な API

MSTest 3.8 以降では、コレクション アサーションには、 Assert.ContainsAssert.DoesNotContainAssert.HasCountAssert.IsEmptyAssert.IsNotEmpty、および Assert.ContainsSingleが含まれます。

MSTest 3.10 以降、比較アサーションには、 Assert.IsInRangeAssert.IsGreaterThanAssert.IsGreaterThanOrEqualToAssert.IsLessThanAssert.IsLessThanOrEqualToAssert.IsPositive、および Assert.IsNegativeが含まれます。

MSTest 3.10 以降では、文字列照合アサーションには、 Assert.StartsWithAssert.EndsWithAssert.MatchesRegexAssert.DoesNotStartWithAssert.DoesNotEndWith、および Assert.DoesNotMatchRegexが含まれます。

MSTest 4.1 以降では、 Assert.IsExactInstanceOfTypeAssert.IsNotExactInstanceOfType は完全に一致する必要があります。 Assert.IsInstanceOfTypeとは異なり、これらのメソッドは派生型と一致しません。

MSTest 4.3 の新しい集合と同値性のアサーション

MSTest 4.3.0 では、次のアサーション メソッドが導入されました。

  • Assert.AreSequenceEqual / Assert.AreNotSequenceEqual — 要素ごとのシーケンス比較。 要素の順序を無視するには、 SequenceOrder.InAnyOrder を渡します。
  • Assert.AreEquivalent / Assert.AreNotEquivalent — 2 つのオブジェクトまたはコレクションの詳細な構造比較。
  • Assert.ContainsAll / Assert.DoesNotContainAll — コレクションに必要なすべての要素が含まれている (または含まれていない) ことをアサートします。
  • Assert.AreAllNotNull — コレクションのすべての要素が非nullであることをアサートします。
  • Assert.AreAllDistinct — コレクションのすべての要素が異なっていることをアサートします。
  • Assert.AreAllOfType — コレクションのすべての要素が想定される型であることをアサートします。

コレクションを比較する場合は、要素ではなく参照を比較する Assert.AreEqualよりも、これらのメソッドを使用します。

MSTest 4.3 では、次の機能も追加されています:

  • Assert.AddValueFormatter を使用して、アサーション エラー メッセージでの値のレンダリング方法をカスタマイズします。
  • Span<T> に対する Memory<T>Assert.HasCount のオーバーロード。
  • 評価された式を含む Assert.IsTrueAssert.IsFalseAssert.IsNull、および Assert.IsNotNull の構造化アサーション エラー メッセージ。
  • 非同期の Assert.ThrowsAsync/Assert.ThrowsExactlyAsync メソッドに対する補間文字列メッセージのオーバーロード、および、そうでなければ待機されない ValueTask<TResult> を返す代理の拒否。

Assert.Scope() を使用したソフトアサーション

Important

Assert.Scope() は実験用 API です。 これを使用すると MSTESTEXP 診断メッセージが出力されます。これは、API の構造と動作が将来のリリースで変更される可能性があることを承知したうえで、(たとえば #pragma warning disable MSTESTEXP を使用するか、プロジェクトの .editorconfig ファイルで)抑制します。

既定では、すべてのアサーションは失敗するとすぐに AssertFailedException をスローし、すぐにテストを終了します。 Assert.Scope()ソフトアサーションが導入されました。スコープがアクティブな間は、アサーションの失敗はスローされず、収集されるため、実行は継続され、スコープ内のすべての失敗を一度に確認できます。 スコープが破棄されると、収集されたエラーがまとめて報告されます。

[TestMethod]
public void ValidatePerson()
{
    using (Assert.Scope())
    {
        Assert.AreEqual("Jane", person.FirstName); // failure collected, execution continues
        Assert.AreEqual("Doe", person.LastName);   // failure collected, execution continues
        Assert.IsTrue(person.IsActive);            // failure collected, execution continues
    }
    // On Dispose, all collected failures are reported together.
}

スコープが破棄された場合:

  • 1 つのエラーが収集された場合は、元の AssertFailedException がスローされます。
  • 複数のエラーが収集された場合、1 つの AssertFailedException がスローされ、それらのすべてが AggregateException に囲われます。

事後条件がスコープ内で適用されない

失敗したアサーションがスコープ内で例外をスローしなくなったため、その後に実行されるコードは、アサーションが成功したことを前提にできません。 これは、null 値の許容や型の縮小など、 すべての 事後条件に適用されます。

using (Assert.Scope())
{
    Assert.IsNotNull(item);
    // 'item' might still be null here: the failure was collected, not thrown.
    Assert.AreEqual("expected", item.Value);
    // 'item.Value' might not equal "expected" either.
}

失敗したアサーションがスコープ内の後の行で NullReferenceException (またはその他の例外) につながる場合、そのセカンダリ例外は、別のバグではなく、既に収集されたエラーの症状です。 スコープが破棄されると、元のアサーション エラーが引き続き報告されます。

値を返すアサーションは、スコープ内で失敗した場合に null/default を返します

一部のアサーションは成功時に値を返します。たとえば、 ThrowsThrowsExactly はキャッチされた例外を返し、 ContainsSingle は一致した要素を返します。 スコープ内でこれらのアサーションのいずれかが失敗すると、エラーが収集され、メソッドはスローするのではなく null/default を返します。

using (Assert.Scope())
{
    // No exception is thrown by the lambda, so the assertion fails. The failure is
    // collected and 'ex' is null. Accessing 'ex' below throws NullReferenceException.
    InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() => { });
    _ = ex.Message; // NullReferenceException—don't use the return value in a scope
}

スコープ内のソフト アサーションによって返される値に依存しないでください。 返された値 (キャッチされた例外など) が必要な場合は、スコープ のアサーションを呼び出すか、スコープが破棄されるまで戻り値に依存しないようにテストを再構築します。

Assert.FailAssert.Inconclusive は常にスローします

FailInconclusive は決して柔らかくはありません。 無条件のテスト結果を表すので、スコープ内であっても常に即時にスローします。 その条件が極めて重要で、それがないと残りのテストを有意義に続けられない場合は、そのいずれかを使用します。

入れ子になったスコープはサポートされていません

Assert.Scope()呼び出しを入れ子にすることはできません。 一度にアクティブにできるアサーション スコープは 1 つだけです。

StringAssert クラス

StringAssert クラスは、文字列の比較と確認に使用します。

Warning

StringAssert クラスは、今後のリリースで非推奨になる可能性があります。 これは下位互換性のためにのみ維持され、新しいコードには推奨されません。 すべての StringAssert メソッドには、 Assert クラスに相当するものがあり、検出可能性が向上します。 既存の使用状況を移行するには、アナライザー のMSTEST0046を参照してください。

使用可能な API は次のとおりです。

CollectionAssert クラス

CollectionAssert クラスは、オブジェクトのコレクションを比較する場合や、コレクションの状態を確認する場合に使用します。

Warning

CollectionAssert クラスは、今後のリリースで非推奨になる可能性があります。 これは主に下位互換性のために維持され、新しいコードには推奨されません。 Assertに同等のメソッド (Assert.ContainsAssert.DoesNotContainAssert.HasCountなど) が存在する場合は、Assertを使用して検出可能性を高めます。

使用可能な API は次のとおりです。

Assert.That を使用してカスタム アサーションを作成する

組み込みのアサーション メソッドでは、すべてのシナリオがカバーされるわけではありません。 独自のチェックを使用してアサーション インフラストラクチャを拡張するために、MSTest は、 Assert.That シングルトン プロパティを拡張フックとして公開します。 Assert インスタンス型に C# 拡張メソッドとしてカスタム アサーションを追加し、呼び出し元は使い慣れたAssert.That.MyAssertion(...)構文で呼び出します。

検出可能性を高めるには、専用の静的クラスでプロジェクト全体のアサーションを整理します。 Assert.Thatによって到達したカスタム アサーションは IntelliSense の組み込みメソッドと共に表示されるため、コンシューマーは別のヘルパー型を覚える必要はありません。

カスタム アサーションを作成する

Assert型を対象とし、条件が失敗したときにAssertFailedExceptionをスローする拡張メソッドを追加します。

using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

public static class CustomAssertExtensions
{
    public static void IsPrime(this Assert assert, int value)
    {
        if (value < 2 || Enumerable.Range(2, (int)Math.Sqrt(value) - 1).Any(i => value % i == 0))
        {
            throw new AssertFailedException($"Assert.That.IsPrime failed. Value <{value}> is not a prime number.");
        }
    }
}

カスタム アサーションを使用する

拡張メソッドを含む名前空間をインポートした後、 Assert.Thatを使用してカスタム アサーションを呼び出します。

[TestMethod]
public void Compute_ReturnsPrime()
{
    int result = _calculator.NextPrime(10);
    Assert.That.IsPrime(result);
}

StringAssertCollectionAssert の拡張フック

StringAssert.ThatプロパティとCollectionAssert.That プロパティは、下位互換性のために同じシングルトン パターンを公開します。 新しいカスタム アサーションの場合は、常に Assert.Thatをターゲットにしてください。 それ以外の場合、ヘルパーはレガシ クラスと同じ検出可能性の問題を継承し、 StringAssertCollectionAssert が非推奨になった場合は移行が必要になります。

Assert.ThatプロパティとメソッドAssert.That(...)

拡張フックとして使用される Assert.That シングルトン プロパティと、MSTest 3.8 で追加された Assert.That(() => condition)method を混同しないでください。 後者はブール式を受け入れ、式ツリー (たとえば、 Assert.That(() => order.Total > 0)) を分析して詳細なエラー メッセージを生成します。 2 つの API は名前を共有しますが、異なる目的で機能します。

ベスト プラクティス

  • 特定のアサーションを使用する: エラー メッセージを改善するために、AreEqualよりもIsTrue(a == b)を優先します。

  • わかりやすいメッセージを含める: 明確なアサーション メッセージを使用して、エラーをすばやく特定するのに役立ちます。

  • 一度に 1 つのことをテストする: 各テスト メソッドでは、1 つの動作を検証する必要があります。

  • Throws / ThrowsExactlyを使用する例外の場合: MSTest v3.8 以降では、Assert.Throws属性よりも、Assert.ThrowsExactlyThrowsAsync、および非同期の対応する (ThrowsExactlyAsyncExpectedException) を優先します。

  • Assert StringAssert /よりもCollectionAssertを優先する: 検出可能性と一貫性を高めるには、Assert クラスを使用します。 StringAssertクラスとCollectionAssert クラスは、今後のリリースで非推奨になる可能性があります。

  • カスタム アサーションの Assert.That を拡張する: 一貫性のある検出可能性を確保するために、 Assert の拡張メソッドとしてカスタム アサーションを追加し、 Assert.Thatを介して呼び出します。 新しいコードで StringAssert.ThatCollectionAssert.That をターゲットにしないでください。

次のアナライザーは、アサーションを適切に使用するのに役立ちます。

  • MSTEST0006 - 属性 ExpectedException 避け、代わりに Assert.Throws メソッドを使用します。
  • MSTEST0017 - アサーション引数は正しい順序で渡す必要があります。
  • MSTEST0023 - ブールアサーションを否定しないでください。
  • MSTEST0025 - 常に誤った条件よりも Assert.Fail を優先します。
  • MSTEST0026 - アサーション引数は条件付きアクセスを回避する必要があります。
  • MSTEST0032 - 常に真のアサート条件を確認します。
  • MSTEST0037 - 適切なアサート メソッドを使用します。
  • MSTEST0038 - 値型での Assert.AreSame は避けてください。
  • MSTEST0039 - 新しい Assert.Throws メソッドを使用します。
  • MSTEST0040 - 非同期 void コンテキストでアサートを使用しないようにします。
  • MSTEST0046 - Assertの代わりにStringAssertを使用します。
  • MSTEST0051 - Assert.Throws には 1 つのステートメントを含める必要があります。
  • MSTEST0053 - 書式設定パラメーター Assert 避けます。
  • MSTEST0058 - catch ブロック内のアサートを回避します。

こちらも参照ください