【问题标题】:How to Mock a class having no default construtor如何模拟没有默认构造函数的类
【发布时间】:2019-02-07 22:34:08
【问题描述】:

我想模拟一个没有默认构造函数的类并在其中调用一个方法。此方法调用同一类的另一个方法。我想设置第二种方法来返回一个值并执行第一种方法的其余部分来测试一些结果。

[Test]
public void TestFunction(){
    int d=0;
    var mockObject = new Mock<Foo>(){MockBehaviour.Default, p, q}; //p and q are parameters for Foo constructor
    mockObject.Setup(x=>x.func2(a,b,c)).Returns(d);
    mockobject.Object.func1();
}


Class Foo{
    public Foo(int x,int y){}

    public virtual int func1(){
       DoSomething;
       func2();
    }

    public virtual int func2(){}
}

我在嘲笑 Foo,因为我不想在测试 func1() 时执行 func2()。因此,我将 mockObject 设置为在 func1() 调用 func2() 时返回 func2() 的值而不执行它。

当我运行这个测试用例时,我得到异常“System.NotSupportedException : Parent 没有默认构造函数。必须显式定义默认构造函数。”

如果我在调试测试用例时看到 mockObject,mockObject.Object 没有被初始化。 我是单元测试和模拟的新手。有人可以帮我解决我哪里出错了。

【问题讨论】:

  • 老实说,听起来你这里有点乱。为什么你的测试关心这个其他方法在做什么?如果它依赖于“其他东西”,那么应该注入该依赖项。这样,您可以非常简单地模拟依赖项。
  • @DavidG 似乎被测系统依赖于它的另一种方法,OP 已经测试过并且想要模拟。
  • 通常你不应该模拟一个被测系统。但是,您可以继承 Foo 并覆盖您想要模拟的方法(这与 Moq 的工作方式基本相同)。但是,这假定您的方法是 virtual in Foo
  • 如果你想对 Foo 类进行单元测试,那么你不应该模拟它。只需创建一个类的实例并在其上调用方法。
  • 您不能使用模拟部分测试一个类。 Mock 是一个模拟对象...不是实际对象...所以它不代表您的类的实际代码。当一个方法调用另一个方法并且它们都是公共的时,你不能模拟它们的输出。

标签: c# nunit moq


【解决方案1】:

您的实际问题的答案:

如何模拟没有默认构造函数的类

您需要使用不同的 Mock ctor 重载,以便将参数传递给非默认 Foo ctor:

  var mockObject = new Mock<Foo>(1, 2);

但是,您的测试策略存在缺陷,我相信这是由于您对 Moq 功能的理解。

Mocks 对于简化您正在测试的复杂依赖项非常有用 - 您可以模拟它们,而不是实例化、处理它们的生命周期。当它们不影响您正在积极测试的代码(正如其他人在这个问题中提到的那样)时,这是可以的 - 这不是您的意图。由于 Func2() 与 func1() 的实现紧密耦合,按照现在的代码编写方式,没有执行 Func1() 而不执行 Func2() 的方法。

您可以更改 Func1() 的函数签名以将 Func2() 作为参数 - 这将允许您修改在测试期间传递给它的函数(并允许您轻松模拟 Func2() 的行为.但是你已经说过这是不可能的。

【讨论】:

  • Func1() 和 Func2() 不是私有的。而且我需要模拟 Foo,因为我不希望在测试 func1 时执行 func2。如果我不模拟它,那么 func2 将在测试不需要的 func1 时被调用。这是 10 年前编写的遗留代码,因此我无法更改代码,因为它会影响整个应用程序。
  • @komalThawani - 你更新了你的问题,将 Func1() 和 Func2() 从私有更改为公开 - 我也会更新我的答案。
【解决方案2】:

this thread: 中建议的那样,您也可以简单地提供论据来回答您发布的问题

var mockObject = new Mock<Foo>(MockBehaviour.Default, FooParameters);
mockObject.Setup(x=>x.func2(a,b,c)).Returns(d);
mockobject.Object.func1();

不过,这不是一个好主意。通常你不应该模拟你的被测系统。这样做可能表明您的班级做得太多,需要重组,以便只做一件事。

但是,在某些情况下,一个类可能有多个相互依赖的 API 成员。考虑一个要测试多个重载的重载链。当然,为最内部的重载(使用大多数参数)编写测试很容易。但是,如果想测试您的其他重载是否正确设置了最内部的参数,那么您确实被卡住了。

void DoSomething()
{
    var param = ...
    DoSomething(param);
}
void DoSomething(int p)
{
    // the most inner overload
}

没有解决这种依赖关系的一般规则。在这些情况下,我倾向于对我的测试系统进行子类化,并用空主体覆盖其 (virtual) 成员 - 这也是 MOQ 内部所做的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-01
    • 2011-06-01
    • 1970-01-01
    • 2023-03-20
    • 2017-02-10
    • 1970-01-01
    • 2016-07-18
    相关资源
    最近更新 更多