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

时间:2014-03-04 09:37:09

标签: unit-testing xunit xunit.net

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

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

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

interface IRepository
{
    object GetById(string id);
    void Set(string id, object value);
}

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

// 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);
}

2 个答案:

答案 0 :(得分:5)

您可以考虑撰写test classes as generic classes

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> { }

这个特例利用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> { }

答案 1 :(得分:0)

您可以通过使用诸如GetThingToBeTested()之类的抽象方法来测试多个工具。

    public abstract class Tester
    {
        public abstract Func<string, string, bool> GetThingToBeTested();

        [Theory]
        [InlineData("carrot", "tarroc")]
        [InlineData("apple", "papel")]
        public void Will_be_a_permutation(string original, string valueToTest)
        {
            GetThingToBeTested()(original, valueToTest).Should().BeTrue();
        }

        [Theory]
        [InlineData("hello", "llloh")]
        public void Will_not_be_a_permutation(string original, string valueToTest)
        {
            GetThingToBeTested()(original, valueToTest).Should().BeFalse();
        }
    }

    public class Sort : Tester
    {
        public override Func<string, string, bool> GetThingToBeTested()
        {
            return IsPermutation_Sort;
        }
    }

    public class Count : Tester
    {
        public override Func<string, string, bool> GetThingToBeTested()
        {
            return IsPermutation_Count;
        }
    }