测试接口的许多实现(TDD)

时间:2015-08-20 17:33:01

标签: c# tdd nunit

创建接口的多个实现以确保符合接口合同时,最佳做法是什么?

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方法吗?

  1. 测试最初不会编译,因为SpecialFoo类不会被定义。

  2. 创建编译但未通过所有测试的最小SpecialFoo实现。

  3. 然后测试将编译但不会通过,因为最小SpecialFoo类不符合接口的期望。

  4. 或者我应该为IFoo的每个新实现逐个重新实施每个测试吗?

1 个答案:

答案 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;)可以在合同测试中实现。