使用 MOQ 模拟功能





0/5 (0投票)
使用 MOQ 模拟功能
这段代码有什么问题?
class Program
{
static void Main(string[] args)
{
DateTime dob = new DateTime(1967, 7, 9);
int days = CalcDays(dob);
Console.WriteLine($”Days: {days}”);
}
private static int CalcDays(DateTime dob)
{
return (DateTime.Today – dob).Days;
}
}
好吧,这段代码可以运行。但是测试会成为一个问题,因为 DateTime.Today
是一个 非确定性函数。这意味着该函数在不同时间调用时可能会返回不同的值。在这种情况下,很明显,明天该函数将返回与今天或昨天不同的值。因此,如果你想为此函数编写测试,则需要每天更改它。实际上,更改测试以使其通过并不是单元测试的真正意义……
我们该如何解决这个问题?
这里需要做一些工作。让我画出我们将要实现的内容
我已经在之前的文章中展示过这种模式。我们将 IClockService
注入到 Date
类中。在实际实现中,我们将 CurrentDate
属性实现为 DateTime.Today
。
现在,我们可以创建我们的测试项目并编写一些测试。在测试中,我们将模拟 IClockService
,以便 CurrentDate
变为确定性的,我们不必每天修改我们的测试。
我们的测试可能如下所示
class FakeClockService : IClockService
{
public DateTime CurrentDate
{
get
{
return new DateTime(1980, 1, 1);
}
}
}
[TestClass()]
public class DateTests
{
[TestMethod()]
public void CalcDaysTest()
{
// arrange
Date dt = new Date(new FakeClockService());
DateTime dob = new DateTime(1967, 7, 9);
// act
int days = dt.CalcDays(dob);
// assert
Assert.AreEqual(4559, days);
}
}
正如你所看到的,我实现了一个 FakeClockService
,它总是返回一个固定的日期。因此,测试我的代码变得容易。
但是,如果我想用不同的 CurrentDate
伪造值来测试我的代码,我将需要为每个不同的值实现接口。在这种简单的情况下,接口包含 1 个简单的方法,所以除了弄乱我的代码之外,这不是一个大问题。但是当你的接口变得更大时,这会花费大量精力。
进入 MOQ
根据 他们的网站,MOQ 是“最流行和友好的 .NET 模拟框架”。我倾向于同意这一点。
设置 MOQ
开始使用 MOQ 很简单:在测试项目中,安装 MOQ Nuget 包。这将设置你的项目以使用 MOQ。有两种可能的方法来做到这一点。
使用 GUI
在 Visual Studio 中,打开 Nuget 包管理器(工具 > Nuget 包管理器 > 管理解决方案的 Nuget 包…)并找到 MOQ,然后安装最新的稳定版本。
使用 NugetPackage Manager 控制台
如果你喜欢打字,请调出 Nuget Package Manager 控制台(工具 > Nuget 包管理器 > 包管理器控制台)。确保在默认项目中,选择了你的测试项目,然后键入
install-package MOQ
现在你将在你的项目引用中找到 2 个额外的引用:Mocking
和 Moq
。
在你的测试中使用 MOQ
[TestMethod()]
public void CalcDaysTestNegative()
{
// arrange
var mock = new Mock<IClockService>();
mock.Setup(d => d.CurrentDate).Returns(new DateTime(1960, 1, 1));
Date dt = new Date(mock.Object);
DateTime dob = new DateTime(1967, 7, 9);
// act
int days = dt.CalcDays(dob);
// assert
Assert.AreEqual(-2746, days);
}
Mock
类位于 Moq
命名空间中,所以不要忘记添加...
using Moq;
...在你的 usings
列表中。
我通过调用 Setup
方法来设置模拟。在这个方法中,我实际上是说:“当有人要求你返回 CurrentDate
时,总是给他们 1960 年 1 月 1 日。”
变量 mock
现在包含 IClockService
的实现。我定义了 CurrentDateproperty
以在 2 行代码中返回 new DateTime(1960, 1, 1)
。这保持了我的代码的整洁,因为我不需要为此情况实现另一个伪造类。所以我的代码保持整洁,我可以轻松地测试我所有的边界情况(例如,当 2 个日期相等时)。
为了完整起见:mock
不是 IClockService
,但这将是 mock.Object
。所以我将 mock.Object
传递给 new Date( )
。现在我有一个用于我的测试的确定性函数!
当你的接口变得更大时,你将会更加欣赏 MOQ。当你的接口有 20 个方法,而你只需要 3 个方法时,你只需要模拟这 3 个方法,而不是全部 20 个。你也可以用同样的方式模拟具有抽象或虚拟函数的类。
结论
这绝不是关于如何使用 MOQ 的完整教程。你可以在 https://github.com/Moq/moq4/wiki/Quickstart 找到由 MOQ 的人自己编写的教程。在那里你可以找到 MOQ 的更多功能。
我只是想表明,使用像 MOQ 这样的模拟框架可以让你编写更简单的测试,这样你就少了一个不编写测试的借口!
还有现在你知道了我的出生日期,所以别忘了寄给我一张卡片!
再一点
我想实现一个简单的 GetAge(…)
函数,这应该很容易。但是当我阅读了 StackOverflow 上的这个线程 后,我决定保持简单,只使用 CalcDays(…)
。叫我懦夫吧。
测试愉快!!