web-dev-qa-db-ja.com

静的メソッドのモック

最近、ユニットテストに Moq を使用し始めました。 Moqを使用して、テストする必要のないクラスをモックアウトします。

通常、静的メソッドはどのように処理しますか?

_public void foo(string filePath)
{
    File f = StaticClass.GetFile(filePath);
}
_

この静的メソッドStaticClass.GetFile()はどのようにモックできますか?

追伸Moqと単体テストでお勧めの読み物をいただければ幸いです。

66
Kevin Meredith

MoqやRhinomocksなどのフレームワークのモックは、オブジェクトのモックインスタンスのみを作成できます。つまり、静的メソッドのモックは不可能です。

Googleを検索 で詳細を確認することもできます。

また、StackOverflow herehere 、および here に関して以前にいくつか質問があります。

36
Pure.Krome

@ Pure.Krome:良い応答ですが、いくつかの詳細を追加します

@Kevin:コードにもたらすことができる変更に応じて、ソリューションを選択する必要があります。
これを変更できる場合、何らかの依存性注入によりコードのテストが容易になります。できない場合は、適切な隔離が必要です。
無料のモックフレームワーク(Moq、RhinoMocks、NMock ...)では、デリゲート、インターフェイス、および仮想メソッドのみをモックできます。したがって、静的メソッド、密閉メソッド、および非仮想メソッドには、3つのソリューションがあります。

  • TypeMock Isolator(すべてをモックできますが、高価です)
  • JustMock of Telerik(新参者、安価だが無料ではない)
  • Moles of Microsoft(分離のための唯一の無料ソリューション)

Molesをお勧めします。これは無料で効率的で、Moqのようなラムダ式を使用するためです。ただ一つの重要な詳細:モグラはモックではなくスタブを提供します。したがって、インターフェイスとデリゲートにはMoqを使用できます;)

モック:インターフェースを実装し、特定のメソッドからスローする例外を返す/例外に動的に値を設定する機能を許可し、チェックする機能を提供するクラス特定のメソッドが呼び出されたかどうか。
スタブ:メソッドが呼び出されたかどうかを確認する機能を提供しないことを除いて、モッククラスに似ています。

43
Jeco

.NETには、MOQおよびその他のモックライブラリを除外する可能性があります。モックする静的メソッドを含むアセンブリでソリューションエクスプローラーを右クリックし、フェイクアセンブリの追加を選択する必要があります。次に、アセンブリの静的メソッドを自由にモックできます。

System.DateTime.Now静的メソッドをモックしたいとします。たとえば、これを次のように実行します。

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => new DateTime(1837, 1, 1);
    Assert.AreEqual(DateTime.Now.Year, 1837);
}

静的プロパティとメソッドごとに同様のプロパティがあります。

16
pt12lol

これは、 Pose nugetから入手可能なライブラリで実現できます。とりわけ静的メソッドをモックできます。テストメソッドでこれを書いてください:

Shim shim = Shim.Replace(() => StaticClass.GetFile(Is.A<string>()))
    .With((string name) => /*Here return your mocked value for test*/);
var sut = new Service();
PoseContext.Isolate(() =>
    result = sut.foo("filename") /*Here the foo will take your mocked implementation of GetFile*/, shim);

詳細については、こちらを参照してください https://medium.com/@tonerdo/unit-testing-datetime-now-in-c-without-using-interfaces-978d372478e8

6
mr100

私はポーズが好きでしたが、既知の issue のように見えるInvalidProgramExceptionのスローを止めることができませんでした。今、私は Smocks を次のように使用しています:

Smock.Run(context =>
{
    context.Setup(() => DateTime.Now).Returns(new DateTime(2000, 1, 1));

    // Outputs "2000"
    Console.WriteLine(DateTime.Now.Year);
});
2
sirdank

テスト目的で外部から設定できるデリゲートを呼び出すために、静的メソッドをリファクタリングするという概念で遊んでいます。

これはテストフレームワークを使用せず、完全に特注のソリューションになりますが、リファクタリングは呼び出し元の署名に影響を与えないため、比較的安全です。

これが機能するためには、静的メソッドにアクセスする必要があるため、System.DateTimeなどの外部ライブラリに対しては機能しません。

以下に、2つのパラメーターを受け取る戻り値型と、戻り値型を持たないジェネリックを含む、いくつかの静的メソッドを作成した例を示します。

メインの静的クラス:

public static class LegacyStaticClass
{
    // A static constructor sets up all the delegates so production keeps working as usual
    static LegacyStaticClass()
    {
        ResetDelegates();
    }

    public static void ResetDelegates()
    {
        // All the logic that used to be in the body of the static method goes into the delegates instead.
        ThrowMeDelegate = input => throw input;
        SumDelegate = (a, b) => a + b;
    }

    public static Action<Exception> ThrowMeDelegate;
    public static Func<int, int, int> SumDelegate;

    public static void ThrowMe<TException>() where TException : Exception, new()
        => ThrowMeDelegate(new TException());

    public static int Sum(int a, int b)
        => SumDelegate(a, b);
}

単体テスト(xUnitおよびShouldly)

public class Class1Tests : IDisposable
{
    [Fact]
    public void ThrowMe_NoMocking_Throws()
    {
        Should.Throw<Exception>(() => LegacyStaticClass.ThrowMe<Exception>());
    }

    [Fact]
    public void ThrowMe_EmptyMocking_DoesNotThrow()
    {
        LegacyStaticClass.ThrowMeDelegate = input => { };

        LegacyStaticClass.ThrowMe<Exception>();

        true.ShouldBeTrue();
    }

    [Fact]
    public void Sum_NoMocking_AddsValues()
    {
        LegacyStaticClass.Sum(5, 6).ShouldBe(11);
    }

    [Fact]
    public void Sum_MockingReturnValue_ReturnsMockedValue()
    {
        LegacyStaticClass.SumDelegate = (a, b) => 6;
        LegacyStaticClass.Sum(5, 6).ShouldBe(6);
    }

    public void Dispose()
    {
        LegacyStaticClass.ResetDelegates();
    }
}
1
Joe_DM

私はこれが少し遅いことを知っていますが、このラウンドアバウトの解決策により、Moqを使用して静的メソッドをモックできました。

これを行うために、1つのメソッドが静的メソッドStaticClass.GetFileを呼び出すクラスを作成しました(Placeholderと呼びましょう)。

public class Placeholder{  

    //some empty constructor

    public File GetFile(){

        File f = StaticClass.GetFile(filePath);
        return f;
    }
}

次に、fooStaticClass.GetFileを呼び出す代わりに、Placeholderのインスタンスを作成し、GetFile関数を呼び出しました。

public void foo(string filePath)
{
    Placeholder p = new Placeholder();
    File f = p.GetFile(filePath);
}

ユニットテストでは、StaticClass.GetFileをモックする代わりに、GetFileクラスの非静的Placeholderメソッドをモックすることができました。

0
Justin Borromeo