为不同的实现重用相同的测试

bit*_*onk 6 unit-testing xunit xunit.net

使用xUnit.net什么是干净的(可读/可理解和可维护的)方法,为同一个接口的多个实现重用相同的测试?

我的测试的行为断言部分始终是相同的(因为接口的所有实现都应该表现相同).对于每次测试运行,SUT都是不同的,对于某些特定测试,排列部分略有不同.

例如,我有以下接口的多个实现(MemoryRepository,FileReposity...).

interface IRepository
{
    object GetById(string id);
    void Set(string id, object value);
}
Run Code Online (Sandbox Code Playgroud)

现在我的测试应该确保所有实现都表现相同:

// all implementations of IRepository must behave like this
// so do this test for all implementations
[Fact]
public void GetRetrievesWhatWasPut()
{
    IRepository sut = new MemoryRepository();
    sut.Set("key", 10);
    var result = sut.Get("key");
    result.Should().Be(10);
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ann 5

您可以考虑将测试类编写为泛型类:

public abstract class IntervalFacts<T>
{
    [Theory, AutoCatalogData]
    public void MinimumIsCorrect(IComparable<T> first, 
        IComparable<T> second)
    {
        var sut = new Interval<T>(first, second);
        IComparable<T> result = sut.Minimum;
        Assert.Equal(result, first);
    }
}

public class DecimalIntervalFacts : IntervalFacts<decimal> { }
public class StringIntervalFacts : IntervalFacts<string> { }
public class DateTimeIntervalFacts : IntervalFacts<DateTime> { }
public class TimSpanIntervalFacts : IntervalFacts<TimeSpan> { }
Run Code Online (Sandbox Code Playgroud)

这个特殊的例子利用了AutoFixture编写具体类的能力,这样可以省去实例化每个类的具体实例的麻烦T.

改变排列阶段更加困难,但是根据您需要做的事情,您可以再次向AutoFixture引入一些约定,以便它根据类型自动更改创建的实例.


对于OP中的类型,您可以编写如下测试:

public abstract class RepositoryFacts<T> where T : IRepository
{
    [Theory, AutoRepositoryData]
    public void GetRetrievesWhatWasPut(T sut)
    {
        sut.Set("key", 10);
        var result = sut.Get("key");
        result.Should().Be(10);
    }        
}

public class MemoryRepositoryFacts : RepositoryFacts<MemoryRepository> { }
public class FileReposityRepositoryFacts : RepositoryFacts<FileReposity> { }
Run Code Online (Sandbox Code Playgroud)