简体   繁体   中英

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

 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]

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
  2. Ensure the taskId is correct
  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.

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. So the test implicitly covers 3 classes. Or do I misunderstand that?

The Clock and Task constructor tests are appropriate, and do not require repetition in CreateTask() tests.

To aggregate the appropriate guidance above, the correct tests for WorkManagementService are as follows:

  • Given a CreateTask() invocation with a null parameter, then ensure the error is reported as expected (eg, throwing an 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)
  • 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)
  • Given a successful repository.Save() invocation, then ensure CreateTask() returns the expected Guid

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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