C# 测试 - 在构造函数调用中模拟 class

C# testing - mock class in contructor call

我是在 Visual Studio 2019 年将 MoqXunit 一起使用的新手。

我想模拟在我测试的 class 的构造函数中调用的 class 的构造函数调用。 我的问题的简单演示:

public class MockedClass : IMockedClass
{
   public MockedClass()
   {
      // don't call this
   }
   public List<string> Function1()
   { /* return a List<string> here */ }
}
public class MyClass
{
   readonly IMockedClass mockedClass = null;
   public MyClass()
   {
      mockedClass = new MockedClass();
   }
   public string Function2()
   { /* return a string here */ }
}
public class MyClassTest
{
   [Fact]
   public string Function2Test()
   {
      var returnMock = new List<string>() { "1", "2", "3" };
      var mockMockedClass = new Mock<IMockedClass>();
      mockMockedClass.Setup(x => x.Function1()).Returns(returnMock);
      var myClass = new MyClass();
      var result = myClass.Function2();
      ...
   }
}

我的问题是,在测试 Function2Test 中,调用 MyClass 的构造函数会调用 MockedClass 的构造函数。 但是我不希望 MockedClass 的构造函数被调用。

如何模拟 MockedClass 的构造函数?

我认为您可能需要稍微修改一下代码。你看,获取接口的模拟本身并不会自动改变其他实现在从代码的其他部分实例化时的行为方式。您通常希望通过 Dependency Injection.

将您的模拟实现注入到测试代码中
public class MyClass
{
    readonly IMockedClass mockedClass = null;
    public MyClass(IMockedClass c)
    {
        mockedClass = c; // now you'll get the correct implemetation
    }
    public string Function2()
    { /* return a string here */ }
}
void Main()
{

        var returnMock = new List<string>() { "1", "2", "3" };
        var mockMockedClass = new Mock<IMockedClass>(); // get your mock here
        mockMockedClass.Setup(x => x.Function1()).Returns(returnMock);
        var myClass = new MyClass(mockMockedClass.Object); // inject it here
        var result = myClass.Function2();
}

然后您需要设置依赖注入容器,以便在实际应用程序代码运行时为您提供具体的 MockedClass。根据你的代码和要求,有heaps of options所以我不会在这里推荐具体的框架。

理论上,您可以在当前代码结构中模拟出 class。我可能会看到您如何选择 Fakes 或花哨的反射技术。然而,与社区公认的最佳实践背道而驰可能需要付出很多努力。

你在模拟依赖方面的方向是正确的,但你缺乏依赖倒置原则的实施。

按原样使用代码,您将 MyClass 绑定到 MockedClass,并且让 MyClass 控制 MockedClass 对象的生命周期。

您应该做的是更改 MyClass 的构造函数以接受 IMockedClass 对象。请注意,您应该传递接口,而不是 class 类型。

public class MyClass
{
   readonly IMockedClass mockedClass = null;
   public MyClass(IMockedClass mockedClass)
   {
      this.mockedClass = mockedClass();
   }
   public string Function2()
   { /* return a string here */ }
}

您的测试代码将是:

public string Function2Test()
   {
      var returnMock = new List<string>() { "1", "2", "3" };
      var mockMockedClass = new Mock<IMockedClass>();
      mockMockedClass.Setup(x => x.Function1()).Returns(returnMock);
      var myClass = new MyClass(mockMockedClass.Object);
      var result = myClass.Function2();
      ...
   }