如何测试隐藏测试数据的服务方法

时间:2012-02-28 19:49:06

标签: c# asp.net-mvc unit-testing controller

想象一下我的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行逻辑。

那你会做什么?

1 个答案:

答案 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);
}

您可以看到,在此示例中,我们能够强制方法在单元测试中使用我们想要的任何日期,以验证方法的正确行为。