如何使用Mock.Of <>语法模拟在构造函数中具有参数的类?

时间:2018-07-19 16:19:17

标签: c# .net unit-testing moq

question向我展示了如何模拟在构造函数中具有参数的类。这是Mock.Of<>上的nice block post,但没有显示如何使用函数语法模拟构造函数。

public class MyClass
{
    public MyClass(IDependency1 dep1, IDependency2 dep2, IDependency3 dep3)
    {}

    public ReturnType MyNewMethod(Tyep1 t1, Type2 t2)
    {
       //1. call to ExistingMethod1();
       //2. call to ExistingMethod2();
       //3. call using the DbContext
       //4. call using the Logger
    }
}

我从第一篇博客文章中得到了类似的东西。

var dep1 = new Mock<IDependency1>(); 
var dep2 = new Mock<IDependency2>();
var dep3 = new Mock<IDependency3>();

object[] arrParams = { dep1.Object, dep2.Object, dep3.Object }
var sut = new Mock<MyClass>(arrParams);

那么如何使用Mock.Of<>语法模拟在构造函数中具有参数的类?

编辑

新方法不仅会调用现有方法,而且还会访问DbContextlogger以及其他服务。因此,除了要测试的方法之外,我还需要模拟其他所有内容。

public class MyClass
{
    public MyClass(MyDbContext context, ISecurityService secService, ILogger logger)
    {}

    public ReturnType1 ExistingMethod1(Type1 t1){}

    public ReturnType2 ExistingMethod2(Type t){}

    public MyEntity MyNewMethod(Tyep1 t1, Type2 t2)
    {
       //1. call to ExistingMethod1(); --> I'll just setup the return value
       //2. call to ExistingMethod2(); --> I'll just setup the return value
       //3. call using the DbContext   --> ???
       //4. call using the Logger      --> ???

       var x = ExistingMethod1(t1);                //1.
       var y = ExistingMethod1(x);                 //2.

       var result context.MyEntities.              //3.
              .Where(e => e.id == y.MyEntityId)
              .ToList();

       return result;
    }
}

2 个答案:

答案 0 :(得分:2)

用Moq进行模拟需要模拟该类以使其具有虚拟方法,或者您可以模拟任何接口。当您使用moq进行模拟时,它将动态创建一个动态实现,因此它不依赖于您的实现。就您而言,您可以做

public class MyClass
{
    public MyClass(IDependency1 dep1, IDependency2 dep2, IDependency3 dep3)
    {}

    public ReturnType MyNewMethod(Tyep1 t1, Type2 t2)
    {
       //1. call to ExistingMethod1(); --> I'll just setup the return value
       //2. call to ExistingMethod2(); --> I'll just setup the return value
       //3. call using the DbContext   --> ???
       //4. call using the Logger      --> ???
    }
}

    Mock<MyClass> mockedObj = new Mock<MyClass>();

    mockedObj.SetUp(x=>x.MyNewMethod()).Returns(objectOfReturnType);

在这里您需要将MyNewMethod虚拟化。 return objectOfReturnType是您创建为测试对象的对象。因此不需要或不需要您的方法主体详细信息。那就是模拟的想法,您正在用假的实现(在本例中是安装程序)来模拟实际的实现。您可以根据测试被测类的方式来改变不同的返回对象。我建议您先阅读单元测试101。

请注意,您正在设置MyNewMethod的行为。您的实现可能会做很多事情,但是您关心的是它的回报。这就是为什么该方法也必须是虚拟的,它会被Moq覆盖并返回您刚刚设置的内容。在内部,该方法可能调用不同的内容...所以您不在乎

您还应该阅读Moq的基础知识,您可以在这里https://github.com/Moq/moq4/wiki/Quickstart

答案 1 :(得分:1)

如果要测试MyClass,则可能要模拟依赖项:

// Arrange
var mockDep1 = new Mock<IDependency1>();
var mockDep2 = new Mock<IDependency2>();
var mockDep3 = new Mock<IDependency3>();

var myTestInstance = new MyClass(mockDep1.Object, mockDep2.Object, mockDep3.Object);

// Act
var result = myTestInstance.DoSomething();

// Assert
Assert.AreEqual(result, myExpectedResult); // check the direct result
mockDep1.Verify(mock => mock.SomeMethodOnMock(It.IsAny<string>()), Times.Once); // SomeMethodOnMock was called once
// ... etc

另一方面,如果 MyClass是您要模拟另一个测试对象的依赖项,那么从中提取接口的最佳方法(例如{{1} }),以便您以更简洁的方式测试您的课程。

Moq允许您模拟非密封的类,但是如果没有其他方法,这是一种故障保护,因为在测试期间可能会执行许多无关的代码,并且您只能设置/验证非密封的虚拟或抽象成员