使用Moq模拟类时,如何仅针对特定方法使用CallBase? [英] When mocking a class with Moq, how can I CallBase for just specific methods?

查看:86
本文介绍了使用Moq模拟类时,如何仅针对特定方法使用CallBase?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我非常感谢Moq的Loose模拟行为,当未设置任何期望时,该行为会返回默认值.这很方便,可以节省我的代码,它还可以用作安全措施:在单元测试期间(只要它们是虚拟的)就不会无意间调用依赖项.

I really appreciate Moq's Loose mocking behaviour that returns default values when no expectations are set. It's convenient and saves me code, and it also acts as a safety measure: dependencies won't get unintentionally called during the unit test (as long as they are virtual).

但是,当所测试的方法恰好是虚拟方法时,我对如何保留这些好处感到困惑.
在这种情况下,我 do 想要为该方法调用真实的代码,同时仍然可以轻松地模拟其余的类.

However, I'm confused about how to keep these benefits when the method under test happens to be virtual.
In this case I do want to call the real code for that one method, while still having the rest of the class loosely mocked.

在搜索中发现的所有内容是,我可以设置mock.CallBase = true来确保调用该方法.但是,这影响了整个班级.我不想这样做,因为这使我对隐藏调用依赖项的类中的所有其他属性和方法感到困惑:如果CallBase为true,那么我要么必须这样做

All I have found in my searching is that I could set mock.CallBase = true to ensure that the method gets called. However, that affects the whole class. I don't want to do that because it puts me in a dilemma about all the other properties and methods in the class that hide call dependencies: if CallBase is true then I have to either

  1. 用于隐藏依赖项的所有属性和方法的设置存根-即使我的测试认为不需要关心那些依赖项,或者
  2. 希望我不会忘记设置任何存根(将来也不会在代码中添加新的依赖项)-风险单元测试会遇到真正的依赖项.

我想我想要的是这样的:
mock.Setup(m => m.VirtualMethod()).CallBase();
这样,当我调用mock.Object.VirtualMethod()时,Moq就会调用真正的实现...

What I think I want is something like:
mock.Setup(m => m.VirtualMethod()).CallBase();
so that when I call mock.Object.VirtualMethod(), Moq calls into the real implementation...

问:使用Moq,当我模拟该类以仅存一些依赖项时,有什么方法可以测试虚拟方法? IE.无需诉诸 CallBase = true 且不必存根所有依赖项?

Q: With Moq, is there any way to test a virtual method, when I mocked the class to stub just a few dependencies? I.e. Without resorting to CallBase=true and having to stub all of the dependencies?

用于说明的示例代码
(使用MSTest,InternalsVisibleTo DynamicProxyGenAssembly2)

Example code to illustrate
(uses MSTest, InternalsVisibleTo DynamicProxyGenAssembly2)

在下面的示例中,TestNonVirtualMethod通过,但TestVirtualMethod失败-返回null.

In the following example, TestNonVirtualMethod passes, but TestVirtualMethod fails - returns null.

public class Foo
{
    public string NonVirtualMethod() { return GetDependencyA(); }
    public virtual string VirtualMethod() { return GetDependencyA();}

    internal virtual string GetDependencyA() { return "! Hit REAL Dependency A !"; }
    // [... Possibly many other dependencies ...]
    internal virtual string GetDependencyN() { return "! Hit REAL Dependency N !"; }
}

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestNonVirtualMethod()
    {
        var mockFoo = new Mock<Foo>();
        mockFoo.Setup(m => m.GetDependencyA()).Returns(expectedResultString);

        string result = mockFoo.Object.NonVirtualMethod();

        Assert.AreEqual(expectedResultString, result);
    }

    [TestMethod]
    public void TestVirtualMethod() // Fails
    {
        var mockFoo = new Mock<Foo>();
        mockFoo.Setup(m => m.GetDependencyA()).Returns(expectedResultString);
        // (I don't want to setup GetDependencyB ... GetDependencyN here)

        string result = mockFoo.Object.VirtualMethod();

        Assert.AreEqual(expectedResultString, result);
    }

    string expectedResultString = "Hit mock dependency A - OK";
}

推荐答案

我相信Lunivore的答案在撰写时是正确的.

I believe Lunivore's answer was correct at the time it was written.

在Moq的较新版本(我认为从2013年的4.1版开始)中,可以使用您建议的确切语法来做您想做的事情.那就是:

In newer versions of Moq (I think since version 4.1 from 2013) it is possible to do what you want with the exact syntax you propose. That is:

mock.Setup(m => m.VirtualMethod()).CallBase();

这将设置宽松的模拟程序,以调用VirtualMethod的基本实现,而不是仅返回default(WhatEver),而是仅对此成员(VirtualMethod).

This sets up the loose mock to call the base implementation of VirtualMethod instead of just returning default(WhatEver), but for this member (VirtualMethod) only.

正如用户BornToCode在注释中指出的那样,如果该方法具有返回类型void ,则此方法将无效.当VirtualMethod为非无效时,Setup调用会给出一个Moq.Language.Flow.ISetup<TMock, TResult>,该继承自Moq.Language.Flow.IReturns<TMock, TResult>CallBase()方法.但是当该方法无效时,我们得到一个Moq.Language.Flow.ISetup<TMock>,它缺少所需的CallBase()方法.

As user BornToCode notes in the comments, this will not work if the method has return type void. When the VirtualMethod is non-void, the Setup call gives a Moq.Language.Flow.ISetup<TMock, TResult> which inherits the CallBase() method from Moq.Language.Flow.IReturns<TMock, TResult>. But when the method is void, we get a Moq.Language.Flow.ISetup<TMock> instead which lacks the desired CallBase() method.

更新:andrew.rockwell在下面指出,它现在可用于void方法,并且显然已在4.10版本(自2018年开始)中进行了修复.

Update: andrew.rockwell notes below that it works now for void methods, and apparently that was fixed in version 4.10 (from 2018).

这篇关于使用Moq模拟类时,如何仅针对特定方法使用CallBase?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆