【问题标题】:Application Layer Testing应用层测试
【发布时间】:2018-07-02 17:49:00
【问题描述】:

我想测试应用层的类,但不确定哪个更好。我的领域模型类是任务,例如

class Task {
   private Clock clock;

   public Guid Id {get; private set;}
   public string Name {get; set;}
   public DateTime StartedDate {get; private set;}

   public Task(Guid id, string name, Clock pClock) {
       Id = id;
       Name = name;
       clock = pClock;
       StartedDate = clock.Now();
   }

}

interface Clock
{
    DateTime Now {get;}
}

所以我想测试 WorkManagementService 类的 CreateTask 方法。我不明白我应该测试什么。假设,我写测试

TestCreateTaskShouldReturnTaskIdWasCreated() 
{
    Guid taskId = Guid.Empty;
    TaskRepository repository = Substitute.For<TaskRepository>();
    repsitory.Add(Arg.Do<Task>(taskArgument => taskId = taskArgument.Id));
    var service = new WorkManagementService(repository);

    var createdTaskId = service.CreateTask("task name");

    Assert.AreNotEqual(Guid.Empty, createdTaskId);
    Assert.AreEqual(taskId, createdTaskId);
}

所以,我不确定这是否是一种好习惯。 CreateTask 方法使用 Task 构造函数来创建 Task 和 Clock 接口的一些实现,因此 WorkManagementService 类依赖于它们。是好方法吗?

如果这令人困惑,我很抱歉。

更新 我在想 CreateTask 方法的第一个实现可能如下。

 class WorkManegementService
{
    private TaskRepository taskRepository;

    public WorkManegementService(TaskRepository pTaskRepository)
    {
        taskRepository = pTaskRepository;
    }

    Guid CreateTask(string name)
    {
        var taskId = Guid.NewGuid();
        var task = new Task(taskId, name, new SystemClock());
        try 
        {
            taskRepository.Save(task);

            return taskId;
        }
        catch (...)
        {
            // some handling
        }
    }
}

在进一步的实施过程中,我将添加任务所有者或类似的东西。

【问题讨论】:

  • 请显示CreateTask 方法和它所在的类。从你的问题中不清楚这个方法与你的测试有多大的关系。
  • @NightOwl888,我已经更新了问题。
  • 您的测试使用Create 方法,而不是CreateTask 方法。如果这不是错字,请同时发布Create 方法。
  • @NightOwl888,这是我的错误,对不起。它使用CreateTask 方法。

标签: .net unit-testing tdd domain-driven-design nsubstitute


【解决方案1】:

您可以测试对象的创建和正确初始化,但这里没有要测试的真正逻辑。 如果您打算测试在哪个上下文中使用此任务,什么行为会更好。

老实说,没有必要对简单的访问器和修改器进行单元测试。这是浪费时间,对任何人都没有帮助。测试创建的唯一原因是因为您希望对象处于特定状态...(例如游戏地图或银行帐户)。

**也许如果您显示创建方法代码,它将更好地概述您的方法应该做什么

[编辑]

您的测试可以验证您的保存方法是否已被调用...    

repository.Received().Save(Arg.Any<Task>()); // Arg.Is if you prefer

更多here

[编辑2]

对于依赖项,您可以完全将任务的初始化委托给另一个方法/类,这样您的测试就可以完全可预测并与其他依赖项隔离。 从纯粹主义者的角度来看,单元测试应该同时测试一件事并且失败的原因是有限的。如果您正在学习,我建议您严格遵守此规则。

Guid CreateTask(string name)
    {
        var task = this.InitTask(name); // or factory.CreateTask(name) or you can have public function prop that you inject if you like functional way...
        try 
        {
            taskRepository.Save(task);

            return taskId;
        }
        catch (...)
        {
            // some handling
        }
    }

【讨论】:

  • 感谢您的回答!我已经用CreateTask 方法的实现更新了这个问题。
  • 好的,谢谢。 CreateTask 方法中对 Task 和 SystemClock 类的依赖关系如何。据我了解,如果 Task 或 SystemClock 构造函数抛出异常,则测试失败。我不确定它是否正确。清楚了吗?
【解决方案2】:

单元测试的主要目的是确保您的应用程序的业务逻辑按预期工作。您必须在示例中测试的唯一业务逻辑是:

  1. 确保方法调用repository.Savetask的内容正确
  2. 确保 taskId 正确
  3. 确保错误处理正常

对于Task 对象,您可以考虑进行测试以确保构造函数正常工作,但更重要的是对您的Clock 类进行单元测试以确保它按预期工作。

【讨论】:

  • 感谢您的回答。我对ClockTask 构造函数进行了测试,但似乎CreateTask 测试的正确执行取决于TaskClock 类。所以测试隐含地涵盖了 3 个类。还是我理解错了?
  • 如果你有多个类在测试,那不再是单元测试,而是集成测试。集成测试用于测试更大的应用程序块,以确保各个部分协同工作。但是单元测试应该伪造除一个被测类之外的所有类,并测试该类中每个重要的业务逻辑。
  • 事实上,单元测试应该总是模拟时间,这样测试才能获得可以断言的一致输出。时间是业务逻辑的另一个依赖项。
  • 那么我该如何为 CreateTask 方法编写单元测试,该方法在内部使用 TaskSystemClock 呢?我不能嘲笑他们。我不确定这是否可能。
【解决方案3】:

在这里你问:

我对 Clock 和 Task 构造函数进行了测试,但似乎是正确的 CreateTask 测试的执行取决于 Task 和 Clock 类。所以 测试隐含地涵盖了 3 个类。还是我理解错了?

ClockTask 构造函数测试是合适的,并且不需要在 CreateTask() 测试中重复。

为了汇总上述适当的指导,WorkManagementService 的正确测试如下:

  • 给定带有 null 参数的 CreateTask() 调用,然后确保按预期报告错误(例如,抛出 ArgumentNullException
  • 鉴于带有良好参数的 CreateTask() 调用,然后确保使用正确的参数调用 repository.Save()(通过使用模拟构造函数注入的存储库)
  • 鉴于具有良好参数的 CreateTask() 调用失败的 repository.Save() 调用(抛出异常),然后确保 CreateTask() 失败为预期(例如,重新引发异常、引发新的外部异常或其他一些预期的错误处理)
  • 假设成功调用repository.Save()然后确保CreateTask()返回预期的Guid

【讨论】:

    猜你喜欢
    • 2013-08-03
    • 2020-02-10
    • 1970-01-01
    • 2021-04-06
    • 1970-01-01
    • 1970-01-01
    • 2013-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多