首页 > 解决方案 > 每个接口或每个实现创建一个完整的测试套件

问题描述

我想知道如果每个接口有多个实现,编写测试的最佳实践是什么。

我是否必须为每个实现复制每个测试,或者我可以创建一个包含有关接口的所有测试的抽象类,然后为每个实现创建一个简单的测试类来扩展我的抽象测试类?

我正在关注 Osherove 写的“单元测试的艺术”,他总是重复说我们必须保持测试和测试套件尽可能简单,所以我不知道添加抽象类是否会增加复杂性阅读其他开发人员的测试。

以下哪个示例被普遍接受且更具可读性?

示例代码:

public interface IMyInterface() 
{
    public string MyMethod(string input);
}

public class MyClassA : IMyInterface 
{
    public string MyMethod(string input) 
    {
        return input;
    }
} 

public class MyClassB : IMyInterface 
{
    public string MyMethod(string input) 
    {
        return "hello from MyClassB";
    }
}

public class MyClassC : IMyInterface 
{
    public string MyMethod(string input) 
    {
        throw new NotImplementedException();
    }
}

测试套件 1:

[TestFixture]
public class MyClassATest {

    [Test]
    public void MyMethod_WhenCalled_ReturnsANotNullString() 
    {
        //arrange
        MyClassA sut = new MyClassA();

        //act
        string result = sut.MyMethod("hi");

        //assert
        Assert.IsNotNull(result);
    }
} 

[TestFixture]
public MyClassBTest {

    [Test]
    public void MyMethod_WhenCalled_ReturnsANotNullString() 
    {
        //arrange
        MyClassA sut = new MyClassB();

        //act
        string result = sut.MyMethod("hi");

        //assert
        Assert.IsNotNull(result);
    }
}

[TestFixture]
public MyClassCTest {

    [Test]
    public void MyMethod_WhenCalled_ReturnsANotNullString() 
    {
        //arrange
        MyClassA sut = new MyClassC();

        //act
        string result = sut.MyMethod("hi");

        //assert
        Assert.IsNotNull(result);
    }
}

测试套件 2:

[TestFixture]
public abstract class IMyInterfaceTest<TImplementation> where TImplementation : IMyInterface {

    [Test]
    public void MyMethod_WhenCalled_ReturnsANotNullString() 
    {
        //arrange
        TImplementation sut = new MyClassC();

        //act
        string result = sut.MyMethod("hi");

        //assert
        Assert.IsNotNull(result);
    }
}

[TestFixure]
public class MyClassATest : IMyInterfaceTest<MyClassA> {}
[TestFixure]
public class MyClassATest : IMyInterfaceTest<MyClassB> {}
[TestFixure]
public class MyClassATest : IMyInterfaceTest<MyClassC> {}

标签: c#unit-testing

解决方案


为什么不使用 TestCaseSource ( https://github.com/nunit/docs/wiki/TestCaseData ) 尝试这种方法。好处是你有一个测试而不是许多重复测试做同样的事情。

        [TestCaseSource(typeof(MyDataClass), "TestCases")]
        public void MyMethod_WhenCalled_ReturnsANotNullString(IMyInterface sut, string value)
    {
        //arrange

        //act
        string result = sut.MyMethod(value);

        //assert
        Assert.IsNotNull(result);
    }

    public class MyDataClass
    {
        public static IEnumerable TestCases
        {
            get
            {
                yield return new TestCaseData(new MyClassA(), "hi");
                yield return new TestCaseData(new MyClassB(), "test");
                yield return new TestCaseData(new MyClassC(), "another");
            }
        }
    }

推荐阅读