【发布时间】:2011-01-05 18:33:16
【问题描述】:
当我为某些类型的对象(例如 Forms 或 UserControls 等 UI 元素)编写测试时,我经常发现自己正在改变我的 TDD 模式;我没有先进行测试,而是定义并布置表单的控件,以提供“骨架”,然后开始编写行为测试(数据绑定/“解除绑定”、显示模式行为等)。在这样做的过程中,我发现自己与非公众成员打交道。我对其他行为方法也遇到了同样的担忧;在担心其他方法中的用法及其行为之前,我可能想专注于并练习由其他方法调用的某个私有助手中的逻辑。
对我来说,将所有内容都公开(有时是虚拟的)只是为了能够对所有内容进行单元测试是一种味道;我不希望其他对象能够调用助手或直接访问文本框;但我需要知道帮助程序完成了它的工作,并且在表单加载时文本框得到了它的值。
我前段时间得出的解决方案是为实际被测对象创建一个“测试代理”。代理派生自被测对象,不会隐藏或覆盖任何行为,但它确实提供内部可见的 getter、setter 和/或方法来调用被测对象的非公共成员,从而使我能够告诉对象执行某些操作,然后我可以查看结果,而无需测试也依赖于对象内的适当集成,或者仅出于测试目的在生产代码中公开方法或其他一些感兴趣的成员。
我看到的优点:
- 成员的可见性并不取决于您是否要进行单元测试。
- 更好地控制您可以在测试中对对象执行哪些操作,从而实现更灵活和可扩展的测试。
我看到的缺点:
- 课程数量增加,需要额外开发一个级别以用于测试目的。
- 必须注意不要以某种方式最终在生产代码中使用测试代理(使构造函数或整个类内部通常可以解决问题)
- 不是像您这样的“纯”单元测试,在某种程度上,它依赖于代理和实际被测对象之间的集成。
问题是,这是构建单元测试的有效方法,还是我必须这样做表明代码或测试策略存在问题?
【问题讨论】:
-
“私人助手”是一个方法,还是一个类?如果它是一个类,那么它的方法应该是公开的,因此可以从外部测试——这很好。如果它是一个方法,考虑把它变成一个类,从而减少到前面的情况。
-
好建议,但并不总是有效。作为一个更具体的例子,我为表单选择的一个常见模式是实现一个 Bind() 方法,该方法的工作是获取一些数据对象并填充包含在表单或嵌套控件上的控件。这种方法需要操作其他非公共成员(嵌套控件),它只对为其编写的一个表单具有价值,并且它是正确初始化表单的一系列步骤,所以我不希望它被调用直接通过生产代码。
-
我错过了什么吗?为了代理您的课程,您是否不需要保护其私有成员?所以你的测试策略迫使你让你的成员比你想要的更容易访问?
-
@David - 是的,这是另一个缺点,但在将消费者指向他们应该使用和不应该使用的方法方面,protected 比 public 更好。另一种方法是将我想要测试的所有内容都公开,这意味着几乎所有内容都公开,当消费者在他们认为应该插入的任何地方插入时,这会造成维护噩梦。
标签: unit-testing tdd