16

目前我开始将 Mock 对象的概念引入到我的单元测试中。特别是我正在使用 Moq 框架。然而,我注意到的一件事是,我正在使用这个框架测试的类突然显示代码覆盖率为 0%。

现在我明白了,因为我只是在模拟这个类,它本身并没有运行实际的类......但是我如何编写这些测试并让代码覆盖率返回准确的结果?我是否必须编写一组使用 Mocks 的测试和一组直接实例化类。

也许我在没有意识到的情况下做错了什么?

这是我尝试对名为“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!";
        }
    }
}

有谁知道我应该做些什么不同的事情?

4

3 回答 3

17

您没有正确使用您的模拟对象。当您使用模拟对象时,您的意思是在不实际使用真实对象的情况下测试您的代码如何与其他对象交互。请看下面的代码:

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()方法会发生什么?

于 2008-12-03T05:41:45.527 回答
9

大错误是嘲笑被测系统(SUT),你测试别的东西。您应该只模拟 SUT 依赖项。

于 2008-12-03T05:26:37.163 回答
3

在您了解这里发生的交互之前,我建议您远离模拟框架。

IMO 最好使用手动创建的测试替身来学习,然后毕业到模拟框架。我的推理:

  1. 模拟框架抽象出实际发生的事情;如果您必须显式创建依赖项,则更容易掌握交互,然后在调试器中进行测试。

  2. 滥用框架很容易。如果你在学习的时候自己动手,你就更有可能理解不同类型的测试替身之间的差异。如果您直接使用模拟框架,则在需要存根时使用模拟很容易,反之亦然——有很大的不同。

可以这样想:被测试的类是焦点。您创建它的一个实例,调用它的方法,然后断言结果是正确的。如果被测类具有依赖关系(例如,构造函数中需要某些东西),您可以使用 A:真实类或 B:测试替身来满足这些依赖关系。

我们使用测试替身的原因是它隔离了被测类,这意味着您可以以更可控的方式运行它的代码。

例如,如果您有一个包含网络对象的类,则如果您被迫使用具体的网络连接对象,则无法测试拥有类的错误处理例程来检测死连接。相反,您注入一个假连接对象,并告诉它在调用其“SendBytes”方法时抛出异常。

即在每个测试中,被测类的依赖关系是专门为执行一段特定的代码而创建的。

于 2009-06-06T02:23:44.473 回答