应用层测试
Application Layer Testing
我想测试 class 应用层,不确定哪个更好。我有 class 领域模型是任务,例如
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 方法 class。我不明白我应该测试什么。假设,我写 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);
}
所以,我不确定这是否是个好习惯。 CreateTask 方法使用 Task 构造函数来创建 Task 和 Clock 接口的一些实现,因此 WorkManagementService class 依赖于它们。有什么好方法吗?
如果这让您感到困惑,我深表歉意。
更新
我认为 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
}
}
}
在进一步实施过程中,我将添加 Task Owner 或类似内容。
您可以测试对象的创建和正确初始化,但这里没有要测试的实际逻辑。
如果您有意测试此任务在哪个上下文中使用,什么行为会更好。
老实说,没有必要对您的简单访问器和修改器进行单元测试。这是浪费时间,对任何人都没有帮助。测试创建的唯一原因是因为您希望对象处于特定状态...(例如游戏地图或银行帐户)。
**如果您显示 Create 方法代码,它可能会更好地概述您的方法应该做什么
[编辑]
您的测试可以验证您的保存方法是否已被调用...
repository.Received().Save(Arg.Any<Task>()); // Arg.Is if you prefer
更多here
[编辑2]
对于依赖项,您可以将任务的初始化完全委托给另一个 method/class,这样您的测试就可以完全可预测并与其他依赖项隔离。
从纯粹的角度来看,单元测试应该一次测试一件事并且失败的原因有限。如果您正在学习,我建议您严格应用此规则。
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
}
}
单元测试的主要目的是确保应用程序的业务逻辑按预期工作。您必须在示例中测试的唯一业务逻辑是:
- 确保方法调用
repository.Save
和 task
的内容正确
- 确保taskId正确
- 确保错误处理正常
至于 Task
对象,您可以考虑进行测试以确保构造函数正常工作,但更重要的是对 Clock
class 进行单元测试以确保它是按预期工作。
这里你问:
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?
Clock
和 Task
构造函数测试是合适的,不需要在 CreateTask()
测试中重复。
为了汇总上述适当的指导,WorkManagementService
的正确测试如下:
- 给定一个带有空参数的
CreateTask()
调用,然后确保按预期报告错误(例如,抛出一个 ArgumentNullException
)
- 给定一个带有良好参数的
CreateTask()
调用,然后确保使用正确的参数调用repository.Save()
参数(通过使用模拟构造函数注入存储库)
- 给定一个带有良好参数的
CreateTask()
调用和一个失败的repository.Save()
调用(抛出的异常) , then 确保 CreateTask()
按预期失败(例如,重新抛出异常、抛出新的外部异常或其他一些预期的错误处理)
- 给定成功的
repository.Save()
调用,然后确保CreateTask()
returns预期的Guid
我想测试 class 应用层,不确定哪个更好。我有 class 领域模型是任务,例如
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 方法 class。我不明白我应该测试什么。假设,我写 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);
}
所以,我不确定这是否是个好习惯。 CreateTask 方法使用 Task 构造函数来创建 Task 和 Clock 接口的一些实现,因此 WorkManagementService class 依赖于它们。有什么好方法吗?
如果这让您感到困惑,我深表歉意。
更新 我认为 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
}
}
}
在进一步实施过程中,我将添加 Task Owner 或类似内容。
您可以测试对象的创建和正确初始化,但这里没有要测试的实际逻辑。 如果您有意测试此任务在哪个上下文中使用,什么行为会更好。
老实说,没有必要对您的简单访问器和修改器进行单元测试。这是浪费时间,对任何人都没有帮助。测试创建的唯一原因是因为您希望对象处于特定状态...(例如游戏地图或银行帐户)。
**如果您显示 Create 方法代码,它可能会更好地概述您的方法应该做什么
[编辑]
您的测试可以验证您的保存方法是否已被调用...
repository.Received().Save(Arg.Any<Task>()); // Arg.Is if you prefer
更多here
[编辑2]
对于依赖项,您可以将任务的初始化完全委托给另一个 method/class,这样您的测试就可以完全可预测并与其他依赖项隔离。 从纯粹的角度来看,单元测试应该一次测试一件事并且失败的原因有限。如果您正在学习,我建议您严格应用此规则。
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
}
}
单元测试的主要目的是确保应用程序的业务逻辑按预期工作。您必须在示例中测试的唯一业务逻辑是:
- 确保方法调用
repository.Save
和task
的内容正确 - 确保taskId正确
- 确保错误处理正常
至于 Task
对象,您可以考虑进行测试以确保构造函数正常工作,但更重要的是对 Clock
class 进行单元测试以确保它是按预期工作。
这里你问:
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?
Clock
和 Task
构造函数测试是合适的,不需要在 CreateTask()
测试中重复。
为了汇总上述适当的指导,WorkManagementService
的正确测试如下:
- 给定一个带有空参数的
CreateTask()
调用,然后确保按预期报告错误(例如,抛出一个ArgumentNullException
) - 给定一个带有良好参数的
CreateTask()
调用,然后确保使用正确的参数调用repository.Save()
参数(通过使用模拟构造函数注入存储库) - 给定一个带有良好参数的
CreateTask()
调用和一个失败的repository.Save()
调用(抛出的异常) , then 确保CreateTask()
按预期失败(例如,重新抛出异常、抛出新的外部异常或其他一些预期的错误处理) - 给定成功的
repository.Save()
调用,然后确保CreateTask()
returns预期的Guid