5

クラス内の特定のプライベート メソッドを内部的に呼び出すクラスのパブリック メソッドがあります。次のようになります。

public class MyClass : IMyClassInterface
{
    public List<int> MyMethod(int a, int b)
    {
        MyPrivateMethod(a, b, ref varList, ref someVal);
    }
    private void MyPrivateMethod(int a, int b, ref List<int> varList, ref double someval)
    {
    }
}

ここで、基本的に NUnit を使用してこのパブリック メソッドをテストしたいと思います。モックには NMock 2.0 を使用しています。どうすればいいのですか?なぜなら、公開したくないこのプライベートメソッドを内部的に呼び出しているからです。または、代わりにプライベートメソッドを保護にすると、それを行う方法はありますか?

4

3 に答える 3

6

さて、私は基本的にこのパブリックメソッドをテストしたいと思います (...)

これは素晴らしい。これはあなたがすべきことです。内部の詳細についてはしばらく忘れてください。public メソッドの観点から、これら 2 つのスニペットに違いはありますか?

// Your current implementation
public void MyMethod(int a, int b)
{
    MyPrivateMethod(a, b);
}
private void MyPrivateMethod(int a, int b)
{
    var c = a + b;
    // some more code
}

// Private method inlined
public void MyMethod(int a, int b)
{
    var c = a + b;
    // some more code
}

誰が呼び出しても (パブリック) MyMethod、これら 2 つの違いに気付くことはできません。最終結果は同じです。パブリック API に関する限り、それは無関係であるため、プライベート メソッド呼び出しがあっても問題ありません。上記のプライベートメソッドをインライン化して、それを永久になくすことができ、パブリックコンシューマーの観点からは何も変わりません。最終結果だけが重要です。コード コンシューマーによって観測可能な最終結果をテストします。内部の意味不明なことではありません。

重要な認識は次のとおりです。

適切に設計された SOLID コードは、プライベートなモックを行う必要があるような立場にあなたを置くことは決してありません。問題の原因は? 悪いデザイン。

ソース:プライベート メソッドをモックする方法 - ソリューション

うん。残念ですが、あなたのデザインはそれほど素晴らしいものではありません。それを変更するかどうかに応じて、いくつかの方法があります。

  • プライベートな詳細を模倣しようとせず、パブリック API に焦点を当てます (設計上の問題には役立ちません)
  • プライベート メソッドをクラスに抽出し、依存関係を導入する (長期的な解決策、設計を改善し、コードを簡単にテストできるようにする)
  • プライベートメソッドを保護し、他の回答で提案されているようにテストでオーバーライドします(設計の問題には役立たず、価値のあるテストが得られない可能性があります)

どちらを選んでも、あなたに任せます。ただし、もう一度強調します。private メソッドのモックは、単体テスト、ライブラリ、またはツールの問題ではありません。これは設計上の問題であり、そのように解決するのが最善です。


ちなみに、(可能であれば)NMock2は使用しないでください。2009 年から最後の変更が加えられた図書館です。15 年前に最後に整備された 30 年前の車を持っているようなものです。最近はもっと優れたものがあります (FakeItEasy、Moq、NSubstitute)。

于 2015-07-09T06:58:28.197 に答える
5

はい、「トリック」は、プライベートの代わりに保護されたものを使用してから、クラスを継承し、保護されたメソッドを実行する新しいクラスでテストを実行することです。これは、ブラウンフィールド コードとレガシー コードをテスト可能にするための非常に一般的な方法です。

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            MyClassTestWrapped t = new MyClassTestWrapped();
            Assert.IsTrue(t.MyPrivateMethod(...));
            Assert.IsTrue(t.MyMethod(...));

            MockFactory _factory = new MockFactory();
            Mock<MyClassTestWrapped> mock;

            mock = _factory.CreateMock<MyClass>();
            mock.Expects.One.MethodWith(d => d.MyPrivateMethod());  // do the nmock magic here


        }
    }

    public class MyClass : IMyClassInterface
    {
        public List<int> MyMethod(int a, int b)
        {
            MyPrivateMethod(a, b, ref varList, ref someVal);
        }
// here change to protected
        protected void MyPrivateMethod(int a, int b, ref List<int> varList, ref double someval)
        {
        }
    }

    public interface IMyClassInterface
    {

    }

    public class MyClassTestWrapped : MyClass
    {
        public List<int> MyMethod(int a, int b)
        {
            base.MyMethod(a, b);
        }

        public List<int> MyPrivateMethod(int a, int b,ref List<int> varList, ref double someval)
        {
            base.MyPrivateMethod(a, b, ref varList, ref someval);
        }

    }
于 2015-07-09T04:57:28.863 に答える
1

現在、プライベート修飾子 (ラッパーなど) を失うためにコードをリファクタリングする必要がありますが、Typemock Isolator のようなツールを使用すると、かなり簡単に行うことができます。

テストを作成するために、例にいくつかのコードを追加しました。

public class MyClass 
{
    public List<int> MyMethod(int a, int b)
    {
        List<int> varList = new List<int>();
        double someVal = 0;

        MyPrivateMethod(a, b, ref varList, ref someVal);

        return varList;
    }

    private void MyPrivateMethod(int a, int b, ref List<int> varList, ref double someval)
    {
    }
}

この単純なアプローチでは、ref パラメータであっても、コード内のプライベート メソッドを偽造するだけです (プロダクションでは変更しません)。

[Test]
public void TestMethod1()
{
    //Arrange
    var myClass = new MyClass();
    var expectedVarList = new List<int> {1,2,3};

    Isolate.NonPublic.WhenCalled(myClass, "MyPrivateMethod")
        .AssignRefOut(expectedVarList, 0.0)
        .IgnoreCall();

    //Act
    var resultVarList = myClass.MyMethod(0, 0);

    //Assert
    CollectionAssert.AreEqual(expectedVarList, resultVarList);

}
于 2015-07-09T07:52:49.420 に答える