web-dev-qa-db-ja.com

単体テストでモックオブジェクトを使用し、コードカバレッジを使用するにはどうすればよいですか?

現在、ユニットテストにモックオブジェクトの概念を導入し始めています。特に、私はMoqフレームワークを使用しています。しかし、私が気づいたことの1つは、このフレームワークを使用してテストしているクラスが突然0%のコードカバレッジを示していることです。

クラスをモックしているだけなので、実際のクラス自体は実行されていないことを理解しました。しかし、これらのテストを記述して、コードカバレッジに正確な結果を返すにはどうすればよいですか?モックを使用するテストのセットと、クラスを直接インスタンス化するためのセットを1つずつ作成する必要がありますか。

おそらく私は気づかずに何か間違ったことをしているのでしょうか?

これは、「MyClass」というクラスをユニットテストしようとしている私の例です。

using Moq;
using NUnitFramework;

namespace MyNameSpace
{
    [TestFixture]
    public class MyClassTests
    {

        [Test]
        public void TestGetSomeString()
        {
            const string EXPECTED_STRING = "Some String!";

            Mock<MyClass> myMock = new Mock<MyClass>();
            myMock.Expect(m => m.GetSomeString()).Returns(EXPECTED_STRING);

            string someString = myMock.Object.GetSomeString();

            Assert.AreEqual(EXPECTED_STRING, someString);
            myMock.VerifyAll();

        }

    }

    public class MyClass
    {
        public virtual string GetSomeString()
        {
            return "Hello World!";
        }
    }
}

誰かが私が違うやり方で何をすべきか知っていますか?

16
mezoid

モックオブジェクトを正しく使用していません。モックオブジェクトを使用している場合、実際のオブジェクトを実際に使用せずに、コードが他のオブジェクトとどのように相互作用するかをテストすることを意図していました。以下のコードを参照してください。

_using Moq;
using NUnitFramework;

namespace MyNameSpace
    {
        [TestFixture]
        public class MyClassTests
        {

            [Test]
            public void TestGetSomeString()
            {
                const string EXPECTED_STRING = "Some String!";

                Mock<IDependance> myMock = new Mock<IDependance>();
                myMock.Expect(m => m.GiveMeAString()).Returns("Hello World");

                MyClass myobject = new MyClass();

                string someString = myobject.GetSomeString(myMock.Object);

                Assert.AreEqual(EXPECTED_STRING, someString);
                myMock.VerifyAll();

            }

        }

        public class MyClass
        {

            public virtual string GetSomeString(IDependance objectThatITalkTo)
            {
                return objectThatITalkTo.GiveMeAString();
            }
        }

        public interface IDependance
        {
            string GiveMeAString();
        }
    }
_

コードが背後にロジックなしで文字列を返すだけの場合は、何も役に立たないようです。

GetSomeString()メソッドがIDependdance .GiveMeAString()メソッドからの戻り値に応じて出力文字列の結果を変更する可能性のあるロジックを実行した場合、真の力が得られます。メソッドがIDependdanceインターフェースから送信される不良データをどのように処理するかを確認できます。

何かのようなもの:

_ public virtual string GetSomeString(IDependance objectThatITalkTo)
 {
     if (objectThatITalkTo.GiveMeAString() == "Hello World")
         return "Hi";
     return null;
 }
_

テストにこの行がある場合:

_myMock.Expect(m => m.GiveMeAString()).Returns(null);
_

GetSomeString()メソッドはどうなりますか?

17
Nathan W

大きな間違いは テスト中のシステム (SUT)をあざけることです、あなたは何か他のものをテストします。 SUTの依存関係のみをモックする必要があります。

8

ここで行われている相互作用を理解するまで、モックフレームワークから離れることをお勧めします。

IMOは、手動で作成したテストダブルで学習し、その後、モックフレームワークに移行することをお勧めします。私の推論:

  1. モックフレームワークは、実際に起こっていることを抽象化します。依存関係を明示的に作成する必要がある場合は、相互作用を把握し、デバッガーでテストに従う方が簡単です。

  2. フレームワークを誤用するのは簡単です。学習中に自分でロールすると、さまざまなタイプのテストダブルの違いを理解する可能性が高くなります。モックフレームワークに直接アクセスすると、スタブが必要なときにモックを使用するのは簡単です。その逆も同様です。大きな違いがあります。

このように考えてください:テスト中のクラスが焦点です。そのインスタンスを作成し、そのメソッドを呼び出してから、結果が正しいことを表明します。テスト対象のクラスに依存関係がある場合(たとえば、コンストラクターで何かが必要な場合)、A:実クラスまたはB:テストダブルのいずれかを使用してそれらの依存関係を満たします。

テストダブルを使用する理由は、テスト対象のクラスを分離するためです。つまり、コードをより制御された方法で実行できます。

例えば。ネットワークオブジェクトを含むクラスがある場合、具体的なネットワーク接続オブジェクトを使用せざるを得ない場合は、接続の切断を検出する所有クラスのエラー処理ルーチンをテストできません。代わりに、偽の接続オブジェクトを挿入し、その「SendBytes」メソッドが呼び出されたときに例外をスローするように指示します。

つまり各テストでは、テスト対象のクラスの依存関係は、特定のコードを実行するために特別に作成されます。

3
Mark Simpson

それは非常に理にかなっています。基本的に、あなたは私が次のことをする必要があると言っています:

public class MyClass
{
    public virtual string GetSomeString(MyOtherClass moc)
    {
        return moc.ToString();
    }
}

.....

Mock<MyOtherClass> myMock = new Mock<MyOtherClass>();

MyClass mc = new MyClass();

string someString = mc.GetSomeString(myMock.Object);
Assert.AreEqual(EXPECTED_STRING, someString);

基本的にSUTをインスタンス化し、SUTが必要とするクラスにモックのみを使用しますか?

0
mezoid