【问题标题】:Distinguish design and implementation detail when doing TDD在进行 TDD 时区分设计和实现细节
【发布时间】:2015-06-19 19:50:37
【问题描述】:

我一直在做单元测试。在做 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)(或其他方法)来确定我是否需要在测试方法中模拟服务。这让人觉得我在编写单元测试时有点考虑实现细节。有什么问题吗?

【问题讨论】:

    标签: unit-testing mocking tdd


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      我可以建议的最佳经验法则是:尝试仅在行为设置/验证是合同的一部分时才使用它。如果您测试行为但您真正感兴趣的是状态,则测试往往会更频繁地中断,因为行为实际上是实现细节。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-04
        相关资源
        最近更新 更多