使用最小起订量仅模拟某些方法


75

我有以下方法:

public CustomObect MyMethod()
{
    var lUser = GetCurrentUser();
    if (lUser.HaveAccess)
    {
        //One behavior
    }
    else 
    {
        //Other behavior
    }

    //return CustomObject
}

我想模拟IMyInterface.GetCurrentUser,以便在调用时MyMethod可以转到其中一个代码路径进行检查。Moq怎么做?

我正在做以下事情:

var moq = new Mock<IMyInterface>();            
moq.Setup(x => x.GetCurrentUser()).Returns(lUnauthorizedUser);

//act
var lResult = moq.Object.MyMethod();

但是由于某些原因lResult,总是如此null,当我尝试进入MyMethod调试程序时,我总是跳到下一条语句。


1
您在哪里lUnauthorizedUser初始化?我想您会想要类似moq.Setup(x => x.GetCurrentUser()).Returns(new User() { HaveAccess = false });
Tyler Treat

1
泰勒(Tyler),确保我没有m setting it in the above code, just didn粘贴它以使代码简短。
Yaroslav Yakovlev

Answers:


157

这被称为部分模拟,而我在moq中知道要执行的方式要求模拟类而不是接口,然后将模拟对象上的“ Callbase”属性设置为“ true”。

这将要求将要测试的类的所有方法和属性都设为虚拟。假设这不是问题,那么您可以编写如下测试:

 var mock = new Mock<YourTestClass>();
 mock.CallBase = true;
 mock.Setup(x => x.GetCurrentUser()).Returns(lUnauthorizedUser);
 mockedTest.Object.MyMethod();

1
太棒了!非常感谢,就t think it这么简单。
Yaroslav Yakovlev

2
我见过类似的答案,但他们忘了提及设置CallBase为真实的,不可或缺的重要部分,谢谢!
Mike de Klerk '18年

1
我听到它的代码味道,只是为了测试而修改您的应用程序代码。您如何看待这种情况?就我而言,现在,我只是在测试一个ASP.Net REST控制器,因此我看不到任何可预见的理由来继承该控制器的子类,但是在其中添加“虚拟”似乎也不成问题。我的方法?
埃文·塞维

1
@RuneStar改造测试是一件棘手的事情-如果您修改代码以使其可测试,那么它就没有测试,因此您无法防止其退化。将这些内容标记为虚拟非常安全。如果确实需要,可以将类包装在接口中(无论如何应该在TDD中进行操作),然后设置要模拟的方法调用以调用真实方法。我认为过大,但是在某些情况下值得在工具箱中使用(例如,您只想让一个方法调用成为真正的实现,而其余方法都被嘲笑了)。

36

扩展李的答案

您无需将类上的所有方法和属性都设置为虚拟,只需要将您要模拟的方法和属性设置为虚拟。

另外,应该指出,您应该嘲笑类的具体实现。

var mock = new Mock<YourTestClass>(); // vs. var mock = new Mock<IYourTestInterface>();

如果您的类没有默认的构造函数,则还需要指定参数以通过以下方式传递给它:

var mock = new Mock<YourTestClass>(x, y, z);
// or
var mock = new Mock<YourTestClass>(MockBehavior.Default, x, y, z);

x, y, z构造函数的第一个,第二个和第三个参数分别在哪里。

最后,如果您要模拟的方法受到保护,则需要包括 Moq.Protected

using Moq.Protected;

TReturnType returnValue = default(TReturnType);

mock.Protected()
    .Setup<TReturnType>("YourMockMethodName", It.IsAny<int>()) // methodname followed by arguments
    .Returns(returnValue);

6
值得注意的是,您可以nameof(YourTestClass.YourMockMethodName)代替“ YourMockMethodName”使用。因此,当您重构方法名称时,测试仍然可以进行。
Shoter

32

我有一个类似的情况。我发现以下代码使我从接口的某些特定实现中更加灵活地使用模拟方法和实际方法:

var mock = new Mock<ITestClass>(); // Create Mock of interface

// Create instance of ITestClass implementation you want to use
var inst = new ActualTestClass();

// Setup to call method of an actual instance
// if method returns void use mock.Setup(...).Callback(...)
mock.Setup(m => m.SomeMethod(It.IsAny<int>())
    .Returns((int x) => inst.SomeMethod(x));

现在,您可以使用实际的方法,但也可以使用类似的方法Verify来查看已被调用多少次。


1
这是一个非常有趣的解决方法,我们无需虚拟类方法就可以实现相同的目的。
Harshad Vekariya

返回null的方法怎么样?不会在模拟对象上调用Returns吗?模拟。设置(m => m.SomeMethod(It.IsAny <int>())。Returns((int x)=> inst.SomeMethod(x));
Bendram

您是否知道一种为实例的所有方法/属性(例如自动而不是一对一设置)进行设置的方法?
veljkoz
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.