想象一下我的asp.net mvc Controller方法可能有这个代码:
testplanService.AddTestplan(testplan,template,release);
调用以下实现的服务方法:
public TestplanService
{
public void AddTestplan(Testplan testplan, Template template, Release release)
{
testplan.CreatedAt = DateTime.Now;
testplan.UserId = WindowsIdentity.GetCurrent().Name;
testplan.Name = release.Name + " " + template.Name + " " + testplan.UserId + " " + testplan.CreatedAt;
_provider.AddTestplan(testplan, template, release);
}
}
如果我不知道,如何测试ASSERT中的CreatedAt,UserId和Name属性 这个方法里面的值?
是的我知道我可以传递testplan对象中的所有3个值,无论如何传递给服务方法,但是mvc中的Controller类不应该有这3行逻辑。
那你会做什么?
答案 0 :(得分:0)
想象一下,我的asp.net mvc Controller方法可以拥有它 代码:
testplanService.AddTestplan(testplan,template,release);
我不想想象这样的事情。正确的方法是你的控制器使用抽象,而不是实际的实现。因此,您在控制器中使用的这个testplanService
变量不是依赖于实际的服务实现,而是一个简单的接口或一个抽象类,它将被注入到控制器的构造函数中。
现在,在您的单元测试中,您实际上可以模拟此接口并单独测试控制器。
现在让我们考虑服务的实际实施:
public void AddTestplan(Testplan testplan, Template template, Release release)
{
testplan.CreatedAt = DateTime.Now;
testplan.UserId = WindowsIdentity.GetCurrent().Name;
testplan.Name = release.Name + " " + template.Name + " " + testplan.UserId + " " + testplan.CreatedAt;
_provider.AddTestplan(testplan, template, release);
}
此方法存在一些问题。首先,它取决于非确定性数据,例如DateTime.Now
难以进行单元测试。 Martin Fowler对单元测试中的非确定性有nice article。它还取决于当前的WindowsIdentity
。所有这些都难以进行单元测试,因为您的方法取决于实际的实现,而不是使用抽象。
因此,您必须抽象出可以在单元测试中模拟的提供者背后的概念。确保所有组件在它们之间弱耦合。你会发现孤立地使用它们要容易得多。
更新:
根据评论部分的要求,这是一个如何抽象日期时间检索的例子:
考虑以下课程:
public class WeekendTester
{
public bool IsWeekendSoon()
{
return (int)DateTime.Now.DayOfWeek > 3;
}
}
显而易见,单元测试IsWeekendSoon
非常困难,因为它取决于您的单元测试运行的那一天。
因此,为了打破此方法中的非确定性,您可以执行以下操作:
public class WeekendTester
{
private readonly Func<DateTime> _dateTimeProvider;
public WeekendTester(Func<DateTime> dateTimeProvider)
{
_dateTimeProvider = dateTimeProvider;
}
public bool IsWeekendSoon()
{
return (int)_dateTimeProvider().DayOfWeek > 3;
}
}
现在您需要的是配置您喜欢的DI框架以将() => DateTime.Now
注入此对象的构造函数中,并且在单元测试中您可以轻松地模拟它:
[TestMethod]
public void Weekend_Is_Still_Far_Away_From_The_28_th_Of_February_2012()
{
// arrange
Func<DateTime> providerMock = () => new DateTime(2012, 2, 28);
var sut = new WeekendTester(providerMock);
// act
var actual = sut.IsWeekendSoon();
// assert
Assert.IsFalse(actual);
}
您可以看到,在此示例中,我们能够强制方法在单元测试中使用我们想要的任何日期,以验证方法的正确行为。