【问题标题】:How to test virtual methods using Moles?如何使用 Moles 测试虚拟方法?
【发布时间】:2011-11-29 05:55:35
【问题描述】:

如何使用 Moles 测试 IsHappy 函数?

class SomeClass
{
     protected virtual bool IsHappy(string mood)
     {
          return (mood == "Happy");
     }
}

我尝试使用 Stub 进行测试:

SSomeClass stub = new SSomeClass();
stub.CallBase = true;
Assert.IsTrue(stub.IsHappyString("Happy"));

...但是 IsHappyString 方法返回 null 从而引发 NullReference 异常。

那么,如何测试IsHappy 方法的默认实现呢?

【问题讨论】:

    标签: c# unit-testing moles


    【解决方案1】:

    我会忘记这里的存根。当您想要伪造依赖项的行为时,存根/模拟是适用的。如果有您想要测试的 SomeClassClient 并且它使用了 SomeClass,您会存根您的 SomeClass:

        public class Foo
        {
            public virtual int GetFoosInt()
            {
                return 12;
            }
        }
        public class FooClient
        {
            private Foo _foo;
    
            public FooClient(Foo foo)
            {
                _foo = foo;
            }
    
            public int AddOneToFoosInt()
            {
                return _foo.GetFoosInt() + 1;
            }
        }
    

    在这个例子中,当测试FooClient时,你要测试的是它返回的比“GetFoosInt()”多一个。您实际上并不关心用于测试 FooClient 的 FoosInt 是什么。因此,您创建一个 Foo 存根,您可以在其中设置 GetFoosInt 以返回您想要的任何内容。

    在你的情况下,测试一个受保护的虚拟成员,我会这样做:

    [TestClass]
    public class SomeClassTest
    {
        private class DummySomeClass : SomeClass
        {
            public bool IsHappyWrapper(string mood)
            {
                return IsHappy(mood);
            }
        }
    
        [TestMethod]
        public void SomeTest()
        {
            var myClass = new DummySomeClass();
    
            Assert.IsTrue(myClass.IsHappyWrapper("Happy"));
        }
    }
    

    这使您可以“直接”访问受保护的虚拟以测试默认行为。唯一需要注意的是,如果您开始定义抽象成员并添加到一般的 SomeClass 中,您还必须将它们添加到这个虚拟继承者中,这会增加测试维护开销。

    我的纯粹主义者说你应该不理会受保护的成员,只通过公共接口测试它们。但是,这在您的情况下可能可行,也可能不可行,而且我真的认为这种方法没有任何害处。

    【讨论】:

    • Moles.NET 中是否有可以“自动化”此操作的工具? Moles 中有一些行为控件(Fallthrough 等)。这些可以用作手动创建 Dummy 类的替代品吗?
    • 我不知道。我主要使用 Moles 来模拟第三方静态方法、文件、GUI 等外部依赖项。我没有将它用作通用模拟引擎,而是选择使用 Moq。鼹鼠非常强大,所以如果这是可能的,我不会感到惊讶,但不幸的是,我不知道它有多离谱。
    • 之前,MSTest 能够生成“访问器”——公开任何受保护/私有成员的特殊程序集。不幸的是,他们似乎取消了对该功能的支持(虽然我不记得我在哪里读过它)。
    【解决方案2】:

    Stubs 和 Moles 用于将类与其所具有的任何依赖项隔离开来,无论是环境依赖项还是类依赖项。这个类没有任何依赖关系,那你为什么要尝试mole或stub呢?

    如果您想确保这个基类在人们覆盖它时正常工作,那么您需要创建一个测试实现。在这种情况下,这或多或少应该是您的测试用例的样子:

    public SomeClassTestAdapter : SomeClass
    {
        public bool GetIsHappy(string mood)
        {
            return IsHappy(mood);
        }
    }
    
    [Test]
    public void ShouldReturnTrueWhenPassedHappy()
    {
        var classUnderTest = new SomeClassTestAdapter();
        bool result = classUnderTest.IsHappy("Happy");
        Assert.IsTrue(result, "Expected result to be true");
    }
    
    [Test]
    public void ShouldReturnFalseWhenPassedLowerCaseHappy()
    {
        var classUnderTest = new SomeClassTestAdapter();
        bool result = classUnderTest.IsHappy("happy");
        Assert.IsFalse(result, "Expected result to be false");
    }
    
    [Test]
    public void ShouldReturnFalseWhenPassedNull()
    {
        var classUnderTest = new SomeClassTestAdapter();
        bool result = classUnderTest.IsHappy(null);
        Assert.IsFalse(result, "Expected result to be false");
    }
    

    等等

    在这段代码中没有应该挤进存根或痣的地方。

    如果您不想为这种情况创建适配器类,则可以使用内置的 .Net 功能,而不是像 Moles 这样的大型付费依赖项。反射和动态使您可以访问受保护或私有成员。看这个例子:

    【讨论】:

    • IsHappy 方法是protected。我无法通过直接从 Class-Under-Test 调用方法来访问它。
    • @Ian:抱歉花了这么长时间才回到这个问题。更新了我的答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多