创建接口的多个实现以确保符合接口合同时,最佳做法是什么?
public interface IFoo {
/// <exception cref="System.ArgumentNullException">
/// If <paramref name="baz"> is <c>null</c>.
/// </exception>
void Bar(Baz baz);
}
我正在考虑以下几点:
public abstract class IFooTestsBase {
protected IFoo Foo;
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void Bar_ThrowsException_WhenBazArgumentIsNull() {
Foo.Bar(null);
}
}
[TestFixture]
public class SpecialFooTests : IFooTestsBase {
[TestFixtureSetUp]
public void Init() {
// Provide instance of `SpecialFoo` for inherited tests.
Foo = new SpecialFoo();
}
// TDD as normal from here...
}
这是一种有效的TDD方法吗?
测试最初不会编译,因为SpecialFoo
类不会被定义。
创建编译但未通过所有测试的最小SpecialFoo
实现。
然后测试将编译但不会通过,因为最小SpecialFoo
类不符合接口的期望。
或者我应该为IFoo
的每个新实现逐个重新实施每个测试吗?
答案 0 :(得分:1)
这是一种非常有效的方法,可确保当前(和将来)的实现者遵守接口的(语义)契约。我第一次听说这类测试的是J.B. Rainsberger,他称他们为contract tests。
如果你依赖于大多数孤立的测试(即大量使用模拟),合同测试是编写集成测试(将几个真实对象粘合在一起)的必要替代方案。隔离单元测试验证单元是否在隔离中正常工作,并且合同测试验证您对协作者的所有假设是否由其实现保证。有关这种孤立测试方式的更多信息,您可以随时阅读J.B&#39的博客。入门的好地方是integrated tests are a scam。
J.B。是我所知道的最核心的TDD之一。他使用TDD风格,专注于外部TDD,仅使用隔离单元测试(即大量使用模拟)。在他的方法中,您测试驱动SUT并为每个协作者创建接口,您在SUT的隔离单元测试中进行模拟。在设计SUT时,您会更深入一级并测试合作者的实施。为了确保这些对象粘合在一起时,他还建议编写这些合同测试作为编写综合测试的替代方法,将实际对象连接在一起:对象为协作者做出的每个假设(例如:&#34;实现此接口的所有对象如果找不到具有此id的人,则返回null,并且#34;)可以在合同测试中实现。