简体   繁体   English

应用层测试

[英]Application Layer Testing

I want to test class of Application Layer and not sure about what is better. 我想测试应用程序层的类,不确定是否更好。 I have class of Domain Model is Task, eg 我有类的领域模型是任务,例如

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

So I want to test CreateTask method of WorkManagementService class. 因此,我想测试WorkManagementService类的CreateTask方法。 I can not understand what I should test. 我不明白我应该测试什么。 Suppose, I write test 假设我写测试

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

So, I'm not sure if it's good practice. 因此,我不确定这是否是一个好习惯。 CreateTask method uses Task constructor to create Task and some implementation of Clock interface, so WorkManagementService class depends on them. CreateTask方法使用Task构造函数创建Task和Clock接口的某些实现,因此WorkManagementService类依赖于它们。 Is a good way? 是个好方法吗?

I'm sorry if this is confusing. 很抱歉,这令人困惑。

UPDATE I'm thinking that the first implementation of CreateTask method may be following. UPDATE我认为可能正在执行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
        }
    }
}

In during further implementation, I'm going to add Task Owner or something similar. 在进一步实施期间,我将添加任务所有者或类似的东西。

You could test the creation and the correct initialization of your object but here there is no real logic to test. 您可以测试对象的创建和正确的初始化,但是这里没有测试的真实逻辑。 It will be better if you have an intention to test in which context this task is used, what behavior. 如果您打算测试在哪个上下文中使用此任务以及什么行为,那就更好了。

Honestly it's unnecessary to unit-test your simple accessors and mutators. 老实说,没有必要对简单的访问器和修改器进行单元测试。 It's a waste of time and it doesn't help anyone. 这是浪费时间,对任何人都没有帮助。 The only reason to test the creation it's because there is a specific state you want your object to be in... (like map of a game or a bank account). 测试创建的唯一原因是因为您希望对象处于特定状态...(例如游戏地图或银行帐户)。

**maybe if you show Create method code it will give a better overview of what your method is supposed to do **也许如果您显示创建方法代码,它将更好地概述您的方法应该做的事情

[Edit] [编辑]

Your test could verify that your save method have been called... 您的测试可以验证您的保存方法已被调用...

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

More here 这里更多

[Edit2] [Edit2]

For the dependencies you could totally delegate the initialization of the task to another method/class, this way your test could be totally predictable and isolated from other dependencies. 对于依赖项,您可以将任务的初始化完全委派给另一个方法/类,这样您的测试就可以完全预测并与其他依赖项隔离。 From a purist point of view, a unit-test should test one thing at the time and have limited reason to fail. 从纯粹的角度来看,单元测试应该同时测试一件事,并且失败的原因有限。 If you are learning I would advise that you apply strictly this rule. 如果您正在学习,我建议您严格应用此规则。

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

The main purpose of unit testing is to ensure the business logic of your application works as expected. 单元测试的主要目的是确保应用程序的业务逻辑按预期工作。 The only business logic you have to test in your example is: 您必须在示例中测试的唯一业务逻辑是:

  1. Ensure the method calls repository.Save and the contents of task are correct 确保方法调用repository.Save并且task内容正确
  2. Ensure the taskId is correct 确保taskId正确
  3. Ensure the error handling is working 确保错误处理正常

As for the Task object, you might consider testing to ensure the constructor works properly, but it is much more important to unit test your Clock class to ensure it is working as expected. 对于Task对象,您可能考虑进行测试以确保构造函数正常工作,但是对Clock类进行单元测试以确保其按预期工作更为重要。

Here you ask: 在这里你问:

I have tests for Clock and Task constructor, but it seems that correct execution of CreateTask test depends on Task and Clock classes. 我已经对Clock和Task构造函数进行了测试,但是似乎CreateTask测试的正确执行取决于Task和Clock类。 So the test implicitly covers 3 classes. 因此,测试隐式涵盖了3个类。 Or do I misunderstand that? 还是我误会了?

The Clock and Task constructor tests are appropriate, and do not require repetition in CreateTask() tests. ClockTask构造函数测试是适当的,并且在CreateTask()测试中不需要重复。

To aggregate the appropriate guidance above, the correct tests for WorkManagementService are as follows: 为了汇总上面的适当指导, WorkManagementService的正确测试如下:

  • Given a CreateTask() invocation with a null parameter, then ensure the error is reported as expected (eg, throwing an ArgumentNullException ) 给定具有null参数的CreateTask()调用, 然后确保按预期报告错误(例如,抛出ArgumentNullException
  • Given a CreateTask() invocation with a good parameter, then ensure repository.Save() is invoked with the proper parameter (by using a mocked constructor-injected repository) 给定具有良好参数的CreateTask()调用, 然后确保使用正确的参数调用repository.Save() (通过使用模拟的构造函数注入的存储库)
  • Given a CreateTask() invocation with a good parameter and a failed repository.Save() invocation (thrown exception), then ensure CreateTask() fails as expected (eg, rethrowing the exception, throwing a new outer exception, or some other expected error handling) 给定一个具有良好参数的CreateTask()调用一个失败的repository.Save()调用(引发异常), 然后确保CreateTask()按预期失败(例如,重新引发异常,引发新的外部异常或其他某些预期的错误)处理)
  • Given a successful repository.Save() invocation, then ensure CreateTask() returns the expected Guid 给定成功的repository.Save()调用, 然后确保CreateTask()返回预期的Guid

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM