2015-06-19 36 views
1

我一直在做单元测试。在做TDD时,我对设计和实现细节之间的界限有点​​困惑。在做TDD时区分设计和实现细节

例如,我有两个接口,服务和适配器,这个过程员工信息(添加,获取,删除...)

public interface IEmployeeService 
{ 
    Employee GetEmployeeById(int id) 
} 

public interface IEmployeeAdapter 
{ 
    private IEmployeeService _service 
    Employee GetEmployeeById(int id) 
} 

在设计,服务从存储如数据库,文件读取数据系统或Web服务,适配器使用服务来获取某些信息。

这个设计看起来很好,直到我开始为适配器编写单元测试。

问题是我需要知道adapter.GetEmployeeById(id)是否会调用service.GetEmployeeById(id)(或其他方法)来确定我是否需要在测试方法中模拟服务。这让我觉得我在编写单元测试时考虑了实现细节。有什么不对的吗?

回答

2

单元测试是white-box测试,因此您完全知道被测系统内部发生了什么。使用这些信息来帮助确定模拟什么是没有问题的。是的,这是一个实现细节,当你的底层实现发生变化时,它可以使你的测试“脆弱”,从而需要改变它。但是在这种情况下,我想知道当我调用adapter.foo()时,它会调用underlyingService.foo(),而mock对此非常合适。

2

我可以建议的最佳经验法则是:只有在它是合同的一部分的情况下才尝试使用行为设置/验证。如果你测试行为,但你实际上感兴趣的是状态,测试往往会更频繁地破坏,因为行为实际上是一个实现细节。

在您的示例中,如果没有人关心服务和适配器之间的确切边界,请随时在适配器类上使用状态验证。但是,如果您的适配器应该将特定的消息调用模式转换为另一组定义明确的消息,则可能需要使用行为验证。换句话说,如果adapter.GetEmployeeById(id)需要转换为service.GetEmployeeById(id)那么它不是一个实现细节。