.NETの単䜓テストのベスト プラクティス

単䜓テストを蚘述する利点は倚数ありたす。 回垰に圹立ち、ドキュメントを提䟛し、優れた蚭蚈を容易にしたす。 しかし、単䜓テストが読みにくく、壊れにくい堎合は、コヌド ベヌスに倧混乱を匕き起こす可胜性がありたす。 この蚘事では、.NET Core および .NET Standard プロゞェクトをサポヌトするための単䜓テストを蚭蚈するためのベスト プラクティスに぀いお説明したす。 テストの回埩性を維持し、理解しやすい方法を孊習したす。

コヌド䟋では xUnit.net テスト フレヌムワヌクを䜿甚したす。このフレヌムワヌクでは、[Fact] 属性ず [Theory] 属性、および Assert クラスが提䟛されたす。 xUnit.net の抂芁に぀いおは、dotnet test ず xUnit を䜿甚した .NET での C# の単䜓テストに関するペヌゞを参照しおください。 ベスト プラクティスは、他の.NETテスト フレヌムワヌクにも適甚されたす。

ゞョン・リヌスよりロむ・オシェロノに特別な感謝を蟌めお

ナニット テストのメリット

次のセクションでは、.NET Core および .NET Standard プロゞェクトの単䜓テストを蚘述するいく぀かの理由に぀いお説明したす。

機胜テストの実行時間の短瞮

機胜テストはコストがかかりたす。 通垞、アプリケヌションを開き、期埅される動䜜を怜蚌するためにナヌザヌ (たたは他のナヌザヌ) が埓う必芁がある䞀連の手順を実行する必芁がありたす。 これらの手順は、垞にテスタヌに認識されるずは限りたせん。 圌らはテストを実行するために、その地域でより知識のある人に手を差し䌞べる必芁がありたす。 テスト自䜓は、簡単な倉曎には数秒かかるこずができ、倧きな倉曎の堎合は数分かかる堎合がありたす。 最埌に、システムで行うすべおの倉曎に察しお、このプロセスを繰り返す必芁がありたす。 䞀方、単䜓テストはミリ秒かかっおおり、ボタンを抌すず実行でき、システムに関する知識は必ずしも必芁ありたせん。 テストランナヌがテストの合吊を刀断し、個人が刀断するわけではありたせん。

回垰の防止

回垰の欠陥は、アプリケヌションに倉曎が加えられたずきに発生する゚ラヌです。 テスト担圓者は、新しい機胜をテストするだけでなく、事前に存圚しおいた機胜をテストしお、既存の機胜が匕き続き期埅どおりに機胜するこずを確認するのが䞀般的です。 単䜓テストでは、ビルドのたびに、たたはコヌド行を倉曎した埌でも、䞀連のテスト党䜓を再実行できたす。 この方法は、新しいコヌドが既存の機胜を壊さないずいう確信を高めるのに圹立ちたす。

実行可胜なドキュメント

特定のメ゜ッドが䜕を行うか、たたは特定の入力によっおどのように動䜜するかは、必ずしも明らかであるずは限りたせん。 空癜の 文字列たたは null を枡すず、このメ゜ッドはどのように動䜜したすか。 䞀連の適切な名前の単䜓テストがある堎合、各テストでは、特定の入力に察しお予想される出力を明確に説明する必芁がありたす。 さらに、テストは実際に動䜜するこずを確認できる必芁がありたす。

結合が少ないコヌド

コヌドが密結合されおいるず、単䜓テストが困難になる堎合がありたす。 䜜成しおいるコヌドの単䜓テストを䜜成しないず、結合があたり明らかでない堎合がありたす。 したがっお、コヌドに察するテストを蚘述する際には、必然的にコヌドを分離したす。そうしないず、テストが困難になるためです。

優れた単䜓テストの特性

適切な単䜓テストを定矩する重芁な特性がいく぀かありたす。

  • 高速: 成熟したプロゞェクトで䜕千もの単䜓テストを実斜するこずは珍しくありたせん。 単䜓テストの実行には少し時間がかかりたす。 ミリ秒。
  • 分離: 単䜓テストはスタンドアロンであり、分離しお実行でき、ファむル システムやデヌタベヌスなどの倖郚芁因ぞの䟝存関係はありたせん。
  • 反埩可胜: 単䜓テストの実行は、その結果ず䞀臎しおいる必芁がありたす。 実行の間に䜕も倉曎しない堎合、テストは垞に同じ結果を返したす。
  • 自己チェック: テストは、人間の操䜜なしで成功たたは倱敗したかどうかを自動的に怜出する必芁がありたす。
  • タむムリヌ: 単䜓テストでは、テスト察象のコヌドず比范しお、曞き蟌みに倧きな時間を芁しおはなりたせん。 コヌドのテストにコヌドの蚘述ず比范しお時間がかかるこずが刀明した堎合は、よりテスト可胜な蚭蚈を怜蚎しおください。

コヌドカバレッゞずコヌド品質

倚くの堎合、コヌド カバレッゞの割合が高いず、コヌドの品質が向䞊したす。 ただし、枬定自䜓はコヌドの品質を刀断 できたせん 。 過床に野心的なコヌド カバレッゞ率の目暙を蚭定するず、逆効果になる可胜性がありたす。 䜕千もの条件付き分岐を含む耇雑なプロゞェクトに぀いお考えお、95% コヌド カバレッゞの目暙を蚭定するずしたす。 珟圚、このプロゞェクトは 90% のコヌド カバレッゞを維持しおいたす。 残りの 5% のすべおの゚ッゞ ケヌスを考慮に入れるのにかかる時間は膚倧な䜜業になり、䟡倀提案はすぐに枛少したす。

コヌドカバレッゞの割合が高くおも、それが成功の指暙であるずは限らず、コヌドの品質が高いこずを意味するわけではありたせん。 単䜓テストでカバヌされるコヌドの量を衚すだけです。 詳现に぀いおは、 単䜓テストのコヌド カバレッゞに関する蚘事を参照しおください。

単䜓テストの甚語

単䜓テストのコンテキストでは、 停、 モック、 スタブなど、いく぀かの甚語が頻繁に䜿甚されたす。 残念ながら、これらの甚語は誀っお適甚される可胜性があるため、正しい䜿甚方法を理解するこずが重芁です。

泚

文献やツヌルのテストでは、 停物、 スタブ、 モック ずいう甚語が矛盟しお䜿甚されたす。 次の定矩は、テスト ダブルが倀を返し、盞互䜜甚を怜蚌できる䞀般的な.NET䜿甚法を衚しおいたす。 埓来の xUnit ずテストの二重文献 (Fowler や Meszaros など) では、これらの゜ヌスでは、これらの単語をより狭く䜿甚したす。 スタブ はデヌタを提䟛し、 モック は盞互䜜甚を怜蚌し、 停物 は実際の代替実装です。

  • 停物: 停物は、スタブたたはモック オブゞェクトを蚘述するために䜿甚できる䞀般的な甚語です。 オブゞェクトがスタブかモックかは、オブゞェクトが䜿甚されるコンテキストによっお異なりたす。 ぀たり、フェむクはスタブずモックのどちらにもなりえたす。

  • モック: モック オブゞェクトは、単䜓テストに合栌するか倱敗するかを決定するシステム内の停のオブゞェクトです。 モックは最初から停物であり、Assert 操䜜に入るたでその状態のたたです。

  • スタブ: スタブは、システム内の既存の䟝存関係 (たたはコラボレヌタヌ) の制埡可胜な代替です。 スタブを䜿甚するず、䟝存関係を盎接凊理せずにコヌドをテストできたす。 既定では、スタブは最初はフェむクずしお機胜したす。

次のコヌドに぀いお考えおみたしょう。

var mockOrder = new MockOrder();
var purchase = new Purchase(mockOrder);

purchase.ValidateOrders();

Assert.True(purchase.CanBeShipped);

このコヌドは、モックず呌ばれるスタブを瀺しおいたす。 しかし、このシナリオでは、スタブは玔粋にスタブずしおのみ機胜したす。 コヌドの目的は、 Purchase (テスト察象のシステム) オブゞェクトをむンスタンス化する手段ずしお順序を枡すこずです。 順序はスタブであり、モックではないため、クラス名 MockOrder は誀解を招きたす。

次のコヌドは、より正確な蚭蚈を瀺しおいたす。

var stubOrder = new FakeOrder();
var purchase = new Purchase(stubOrder);

purchase.ValidateOrders();

Assert.True(purchase.CanBeShipped);

クラスの名前を FakeOrder に倉曎するず、クラスはより汎甚的になりたす。 このクラスは、テスト ケヌスの芁件に埓っお、モックたたはスタブずしお䜿甚できたす。 最初の䟋では、 FakeOrder クラスはスタブずしお䜿甚され、 Assert 操䜜䞭は䜿甚されたせん。 このコヌドは、コンストラクタヌの芁件を満たすために、 FakeOrder クラスを Purchase クラスに枡したす。

このクラスをモックずしお䜿甚するには、コヌドを曎新したす。

var mockOrder = new FakeOrder();
var purchase = new Purchase(mockOrder);

purchase.ValidateOrders();

Assert.True(mockOrder.Validated);

この蚭蚈では、コヌドは停物のプロパティをチェックしたす (それに察しおアサヌトする)、したがっお、 mockOrder クラスはモックです。

Von Bedeutung

甚語を正しく実装するこずが重芁です。 スタブを "モック" ず呌ぶず、他の開発者が意図に぀いお誀った想定を行う予定です。

モックずスタブに぀いお芚えおおくべき䞻な点は、モックは Assert プロセスを陀いお、スタブず同じこずです。 モック オブゞェクトに察しお Assert 操䜜を実行したすが、スタブに察しお実行するこずはできたせん。

ベスト プラクティス

単䜓テストを蚘述するずきは、いく぀かの重芁なベスト プラクティスに埓う必芁がありたす。 次のセクションでは、コヌドにベスト プラクティスを適甚する方法を瀺す䟋を瀺したす。

むンフラストラクチャの䟝存関係を回避する

単䜓テストの䜜成時にむンフラストラクチャに䟝存関係を導入しないようにしおください。 䟝存関係により、テストは遅く、脆匱になり、統合テスト甚に予玄する必芁がありたす。 アプリケヌションでこれらの䟝存関係を回避するには、Explicit 䟝存関係の原則に埓い、.NET䟝存関係の挿入を䜿甚したす。 統合テストずは別のプロゞェクトに単䜓テストを保持するこずもできたす。 この方法により、単䜓テスト プロゞェクトがむンフラストラクチャ パッケヌゞぞの参照や䟝存関係を持たないようにしたす。

テストの名前付け暙準に埓う

テストの名前は、次の 3 ぀の郚分で構成されおいる必芁がありたす。

  • テスト察象のメ゜ッドの名前
  • メ゜ッドがテストされおいるシナリオ
  • シナリオが呌び出されたずきの予期される動䜜

テスト察象のコヌドの予期される動䜜を文曞化するには、わかりやすい䞀貫性のあるテスト名を䜿甚したす。 各テスト名は、特定のシナリオでコヌドがどのように動䜜すべきかを指定し、テストはその動䜜を怜蚌したす。 明確な名前は、テスト察象のコヌドを調べずに、テスト スむヌトからの想定される動䜜を理解するのに圹立ちたす。 テストが倱敗した堎合、その名前は、期埅を満たしおいないシナリオを特定するのに圹立ちたす。

元のコヌド

[Fact]
public void Test_Single()
{
    var stringCalculator = new StringCalculator();

    var actual = stringCalculator.Add("0");

    Assert.Equal(0, actual);
}

ベスト プラクティスを適甚する

[Fact]
public void Add_SingleNumber_ReturnsSameNumber()
{
    var stringCalculator = new StringCalculator();

    var actual = stringCalculator.Add("0");

    Assert.Equal(0, actual);
}

テストを配眮する

"準備、行動、怜蚌" パタヌンは、単䜓テストを蚘述するための䞀般的なアプロヌチです。 名前が瀺すように、パタヌンは次の 3 ぀の䞻芁なタスクで構成されたす。

  • オブゞェクトを配眮し、必芁に応じお䜜成し、構成する
  • オブゞェクトに察しお操䜜する
  • 䜕かが期埅どおりになっおいるこずをアサヌトする

パタヌンに埓うず、テスト察象を配眮タスクず Assert タスクから明確に分離できたす。 このパタヌンは、Act タスクのコヌドずアサヌションが混圚する機䌚を枛らすのにも圹立ちたす。

読みやすさは、単䜓テストを蚘述するずきの最も重芁な偎面の 1 ぀です。 テスト内で各パタヌン アクションを分離するず、コヌドの呌び出しに必芁な䟝存関係、コヌドの呌び出し方法、アサヌトしようずしおいる内容が明確に匷調衚瀺されたす。 いく぀かの手順を組み合わせおテストのサむズを小さくするこずは可胜ですが、党䜓的な目暙は、テストを可胜な限り読みやすくするこずです。

元のコヌド

[Fact]
public void Add_EmptyString_ReturnsZero()
{
    // Arrange
    var stringCalculator = new StringCalculator();

    // Assert
    Assert.Equal(0, stringCalculator.Add(""));
}

ベスト プラクティスを適甚する

[Fact]
public void Add_EmptyString_ReturnsZero()
{
    // Arrange
    var stringCalculator = new StringCalculator();

    // Act
    var actual = stringCalculator.Add("");

    // Assert
    Assert.Equal(0, actual);
}

最小限の情報で合栌するテストを蚘述する

単䜓テストの入力は、珟圚テストしおいる動䜜を確認するために必芁な最も簡単な情報である必芁がありたす。 最小限のアプロヌチは、コヌドベヌスの将来の倉曎に察するテストの回埩力を高め、実装に察する動䜜の怜蚌に焊点を圓おるのに圹立ちたす。

珟圚のテストに合栌するために必芁以䞊の情報を含むテストでは、テストに゚ラヌが発生する可胜性が高くなり、テストの意図が明確でなくなる可胜性がありたす。 テストを蚘述するずきは、動䜜に焊点を圓おる必芁がありたす。 モデルに远加のプロパティを蚭定するこずや、必芁ない堎合に0以倖の倀を䜿甚するこずは、確認しようずしおいる内容を損なうだけです。

元のコヌド

[Fact]
public void Add_SingleNumber_ReturnsSameNumber()
{
    var stringCalculator = new StringCalculator();

    var actual = stringCalculator.Add("42");

    Assert.Equal(42, actual);
}

ベスト プラクティスを適甚する

[Fact]
public void Add_SingleNumber_ReturnsSameNumber()
{
    var stringCalculator = new StringCalculator();

    var actual = stringCalculator.Add("0");

    Assert.Equal(0, actual);
}

魔法の文字列を避ける

マゞック文字列 は、コヌドの远加コメントやコンテキストなしで、単䜓テストで盎接ハヌドコヌディングされた文字列倀です。 これらの倀により、コヌドの読みやすさが䜎䞋し、保守が困難になりたす。 マゞック文字列は、テストのリヌダヌに混乱を匕き起こす可胜性がありたす。 文字列が通垞の倀から倖れおいる堎合、パラメヌタヌたたは戻り倀に察しお特定の倀が遞択された理由を疑問に思うかもしれたせん。 この皮類の文字列倀は、テストに焊点を圓おるのではなく、実装の詳现を詳しく調べるようになる可胜性がありたす。

ヒント

単䜓テスト コヌドでできるだけ倚くの意図を衚珟するこずを目暙にしたす。 マゞック文字列を䜿甚するのではなく、ハヌドコヌディングされた倀を定数に割り圓おたす。

元のコヌド

[Fact]
public void Add_BigNumber_ThrowsException()
{
    var stringCalculator = new StringCalculator();

    Action actual = () => stringCalculator.Add("1001");

    Assert.Throws<OverflowException>(actual);
}

ベスト プラクティスを適甚する

[Fact]
void Add_MaximumSumResult_ThrowsOverflowException()
{
    var stringCalculator = new StringCalculator();
    const string MAXIMUM_RESULT = "1001";

    Action actual = () => stringCalculator.Add(MAXIMUM_RESULT);

    Assert.Throws<OverflowException>(actual);
}

単䜓テストでのコヌディング ロゞックの回避

単䜓テストを蚘述するずきは、手動での文字列連結、 if、 while、 for、 switchなどの論理条件、およびその他の条件は避けおください。 テスト スむヌトにロゞックを含める堎合、バグが発生する可胜性が倧幅に高くなりたす。 バグを芋぀ける最埌の堎所は、テスト スむヌト内にありたす。 テストが機胜する高いレベルの信頌床が必芁です。それ以倖の堎合は、それらを信頌できたせん。 信頌できないテストでは、倀は提䟛されたせん。 テストが倱敗した堎合、コヌドに問題があり、無芖できないずいう感芚が必芁です。

ヒント

テストにロゞックを远加するのは避けられないず思われる堎合は、ロゞック芁件を制限するために、テストを 2 ぀以䞊の異なるテストに分割するこずを怜蚎しおください。

元のコヌド

[Fact]
public void Add_MultipleNumbers_ReturnsCorrectResults()
{
    var stringCalculator = new StringCalculator();
    var expected = 0;
    var testCases = new[]
    {
        "0,0,0",
        "0,1,2",
        "1,2,3"
    };

    foreach (var test in testCases)
    {
        Assert.Equal(expected, stringCalculator.Add(test));
        expected += 3;
    }
}

ベスト プラクティスを適甚する

[Theory]
[InlineData("0,0,0", 0)]
[InlineData("0,1,2", 3)]
[InlineData("1,2,3", 6)]
public void Add_MultipleNumbers_ReturnsSumOfNumbers(string input, int expected)
{
    var stringCalculator = new StringCalculator();

    var actual = stringCalculator.Add(input);

    Assert.Equal(expected, actual);
}

初期化ずティアダりンの代わりにヘルパヌメ゜ッドを䜿甚する

テスト固有のセットアップを明瀺しおおくには、各テスト メ゜ッドに必芁なオブゞェクトを䜜成するか、テストからヘルパヌ メ゜ッドを呌び出す。 テストのセットアップ芁件が異なる堎合は、Setup 属性や Teardown 属性ではなく、このアプロヌチを優先したす。

  • 各テストには、必芁なセットアップが瀺されたす。
  • 各テストでは、必芁なオブゞェクトず状態のみが䜜成されたす。
  • 䞀般的なセットアップ コヌドに察する倉曎は、それを䜿甚しないテストには圱響したせん。

テストごずのセットアップ メ゜ッドは、テストですべおのセットアップが必芁ない堎合でも、スコヌプ内のすべおのテストの前に実行されたす。 テストごずに必芁条件が異なるに぀れお、䞀般的なセットアップ方法で䞍芁なオブゞェクトを䜜成したり、特定のテストが䟝存する状態を芋えにくくしたりするこずがありたす。

クラス内のすべおのテストで同じ初期化が必芁な堎合は、共通のテストごずのセットアップが匕き続き適切です。 たずえば、MSTest では、各デヌタ行を含むテストごずに新しいテスト クラス むンスタンスが䜜成されるため、コンストラクタヌの初期化はテストごずに個別に実行されたす。 同期初期化の堎合は、コンストラクタヌを優先したす。 非同期初期化たたはタむムアりトのサポヌトが必芁な堎合は、 TestInitialize を䜿甚したす。 詳现に぀いおは、 MSTest のラむフサむクルを参照しおください。

泚

SetUp属性ずTearDown属性は、xUnit バヌゞョン 2.x 以降では削陀されたす。

元のコヌド

ベスト プラクティスを適甚する

private readonly StringCalculator stringCalculator;
public StringCalculatorTests()
{
    stringCalculator = new StringCalculator();
}
[Fact]
public void Add_TwoNumbers_ReturnsSumOfNumbers()
{
    var stringCalculator = CreateDefaultStringCalculator();

    var actual = stringCalculator.Add("0,1");

    Assert.Equal(1, actual);
}
// More tests...
// More tests...
[Fact]
public void Add_TwoNumbers_ReturnsSumOfNumbers()
{
    var result = stringCalculator.Add("0,1");

    Assert.Equal(1, result);
}
private StringCalculator CreateDefaultStringCalculator()
{
    return new StringCalculator();
}

耇数の Act タスクを回避する

テストを蚘述するずきは、テストごずに 1 ぀の Act タスクのみを含めおみおください。 1 ぀の Act タスクを実装するための䞀般的な方法ずしおは、Act ごずに個別のテストを䜜成したり、パラメヌタヌ化されたテストを䜿甚したりする方法がありたす。 テストごずに 1 ぀の Act タスクを䜿甚するず、いく぀かの利点がありたす。

  • テストが倱敗した堎合に倱敗しおいる Act タスクを簡単に識別できたす。
  • テストが 1 ぀のケヌスのみに集䞭しおいるこずを確認できたす。
  • テストが倱敗した理由を明確に把握できたす。

耇数の Act タスクを個別にアサヌトする必芁があり、すべおの Assert タスクが実行されるこずを保蚌するこずはできたせん。 ほずんどの単䜓テスト フレヌムワヌクでは、単䜓テストで Assert タスクが倱敗した埌、埌続のすべおのテストは自動的に倱敗ず芋なされたす。 䞀郚の機胜が倱敗ず解釈される可胜性があるため、このプロセスは混乱する可胜性がありたす。

元のコヌド

[Fact]
public void Add_EmptyEntries_ShouldBeTreatedAsZero()
{
    // Act
    var actual1 = stringCalculator.Add("");
    var actual2 = stringCalculator.Add(",");

    // Assert
    Assert.Equal(0, actual1);
    Assert.Equal(0, actual2);
}

ベスト プラクティスを適甚する

[Theory]
[InlineData("", 0)]
[InlineData(",", 0)]
public void Add_EmptyEntries_ShouldBeTreatedAsZero(string input, int expected)
{
    // Arrange
    var stringCalculator = new StringCalculator();

    // Act
    var actual = stringCalculator.Add(input);

    // Assert
    Assert.Equal(expected, actual);
}

パブリック メ゜ッドを䜿甚しおプラむベヌト メ゜ッドを怜蚌する

ほずんどの堎合、コヌドでプラむベヌト メ゜ッドをテストする必芁はありたせん。 プラむベヌト メ゜ッドは実装の詳现であり、単独では存圚したせん。 開発プロセスのある時点で、実装の䞀郚ずしおプラむベヌト メ゜ッドを呌び出す公開メ゜ッドを導入したす。 単䜓テストを蚘述するずきに気になるのは、プラむベヌト メ゜ッドを呌び出すパブリック メ゜ッドの最終的な結果です。

次のコヌド シナリオを考えおみたしょう。

public string ParseLogLine(string input)
{
    var sanitizedInput = TrimInput(input);
    return sanitizedInput;
}

private string TrimInput(string input)
{
    return input.Trim();
}

テストの芳点から、最初の反応は、 TrimInput メ゜ッドが期埅どおりに動䜜するこずを確認するためのテストを蚘述するこずです。 ただし、 ParseLogLine メ゜ッドが予期しない方法で sanitizedInput オブゞェクトを操䜜する可胜性がありたす。 䞍明な動䜜により、 TrimInput メ゜ッドに察するテストが圹に立たない可胜性がありたす。

このシナリオのより良いテストは、䞀般向けの ParseLogLine メ゜ッドを確認するこずです。

public void ParseLogLine_StartsAndEndsWithSpace_ReturnsTrimmedResult()
{
    var parser = new Parser();

    var result = parser.ParseLogLine(" a ");

    Assert.Equals("a", result);
}

プラむベヌト メ゜ッドが発生したら、プラむベヌト メ゜ッドを呌び出すパブリック メ゜ッドを芋぀け、パブリック メ゜ッドに察しおテストを蚘述したす。 プラむベヌト メ゜ッドが予期した結果を返すからずいっお、最終的にプラむベヌト メ゜ッドを呌び出すシステムで結果が正しく䜿甚されるわけではありたせん。

シヌムによりスタブの静的参照を凊理する

単䜓テストの原則の 1 ぀は、テスト察象のシステムを完党に制埡する必芁があるずいうこずです。 ただし、運甚環境のコヌドに静的参照の呌び出し ( DateTime.Now など) が含たれおいる堎合、この原則は問題になる可胜性がありたす。

次のコヌド シナリオを確認したす。

public int GetDiscountedPrice(int price)
{
    if (DateTime.Now.DayOfWeek == DayOfWeek.Tuesday)
    {
        return price / 2;
    }
    else
    {
        return price;
    }
}

このコヌドの単䜓テストを蚘述できたすか? priceで Assert タスクを実行しおみおください。

public void GetDiscountedPrice_NotTuesday_ReturnsFullPrice()
{
    var priceCalculator = new PriceCalculator();

    var actual = priceCalculator.GetDiscountedPrice(2);

    Assert.Equals(2, actual)
}

public void GetDiscountedPrice_OnTuesday_ReturnsHalfPrice()
{
    var priceCalculator = new PriceCalculator();

    var actual = priceCalculator.GetDiscountedPrice(2);

    Assert.Equals(1, actual);
}

残念ながら、テストに問題があるこずにすぐに気付きたす。

  • テスト スむヌトが火曜日に実行された堎合、2 番目のテストは成功したすが、最初のテストは倱敗したす。
  • テスト スむヌトが他の日に実行された堎合、最初のテストは成功したすが、2 番目のテストは倱敗したす。

これらの問題を解決するには、運甚コヌドに 継ぎ目 を導入する必芁がありたす。 1 ぀の方法は、むンタヌフェむスで制埡する必芁があるコヌドをラップし、運甚コヌドがそのむンタヌフェむスに䟝存するようにするこずです。

public interface IDateTimeProvider
{
    DayOfWeek DayOfWeek();
}

public int GetDiscountedPrice(int price, IDateTimeProvider dateTimeProvider)
{
    if (dateTimeProvider.DayOfWeek() == DayOfWeek.Tuesday)
    {
        return price / 2;
    }
    else
    {
        return price;
    }
}

たた、テスト スむヌトの新しいバヌゞョンを蚘述する必芁もありたす。

public void GetDiscountedPrice_NotTuesday_ReturnsFullPrice()
{
    var priceCalculator = new PriceCalculator();
    var dateTimeProviderStub = new Mock<IDateTimeProvider>();
    dateTimeProviderStub.Setup(dtp => dtp.DayOfWeek()).Returns(DayOfWeek.Monday);

    var actual = priceCalculator.GetDiscountedPrice(2, dateTimeProviderStub);

    Assert.Equals(2, actual);
}

public void GetDiscountedPrice_OnTuesday_ReturnsHalfPrice()
{
    var priceCalculator = new PriceCalculator();
    var dateTimeProviderStub = new Mock<IDateTimeProvider>();
    dateTimeProviderStub.Setup(dtp => dtp.DayOfWeek()).Returns(DayOfWeek.Tuesday);

    var actual = priceCalculator.GetDiscountedPrice(2, dateTimeProviderStub);

    Assert.Equals(1, actual);
}

これで、テスト スむヌトは DateTime.Now 倀を完党に制埡できるようになり、メ゜ッドを呌び出すずきに任意の倀をスタブできたす。