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

C# 如何测试隐藏测试数据的服务方法,c#,asp.net-mvc,unit-testing,controller,C#,Asp.net Mvc,Unit Testing,Controller,想象一下,我的asp.net mvc控制器方法可能有以下代码: testplanService.AddTestplan(testplan,template,release); 它调用以下实现的服务方法: public TestplanService { public void AddTestplan(Testplan testplan, Template template, Release release) { testplan.CreatedAt =

想象一下,我的asp.net mvc控制器方法可能有以下代码:

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

}
当我不知道时,如何测试断言中的CreatedAt、UserId和Name属性 此方法中的值是多少

是的,我知道我可以在testplan对象内传递所有3个值,该对象无论如何都会传递给服务方法,但是mvc中的控制器类不应该有这3行逻辑

那你会怎么做

想象一下,我的asp.net mvc控制器方法可以有这样的功能 代码:

testplanService.AddTestplan(测试计划、模板、发布)

我不想想象那样的事情。正确的方法是控制器使用抽象,而不是实际实现。因此,您在控制器中使用的
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。现在
很难进行单元测试。马丁·福勒(Martin Fowler)对单元测试中的非确定性有着深刻的认识。它还取决于当前的
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;
    }
}

您可以看到,在本例中,我们可以强制该方法在单元测试中以我们希望的任何日期工作,以验证该方法的正确行为。

谁说我不使用ITestPlance?除了最后两段,你说的我都知道。最后一段听起来很理论,我不习惯嘲笑。您有小代码示例吗?:-)@帕斯卡,当然,对不起,如果我的最后一段听起来像理论的话。我已经更新了我的答案,以举例说明理论并将其付诸实践。好的,谢谢你现在给我举个例子。看起来不错,但是。。。我认为这与此相关:“所有这些都很难进行单元测试,因为你在这个方法中混合了很多责任。”我应该混合哪些责任(我应该从服务中删除这3行吗?如果是,放在哪里?),你会如何解决它们?我想我不应该把这3行放在控制器中,因为控制器只处理请求/响应和视图设置等…@Pascal,对不起,可能我的措辞不正确。我实际上的意思是,您的方法依赖于具体的实现,而不是抽象。我会更新我的帖子,让这更清楚。好的,谢谢Darin,所有这些都是tobaco给我的,还有新的东西要学。它必须在接下来的几天内稳定下来。我读过的书看起来既好又有趣。有些我理解,有些会来:)
[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);
}