在抽象通用测试类中定义单元测试

时间:2011-07-15 08:06:18

标签: c# visual-studio unit-testing generics abstract-class

是否可以在Visual Studio 2008中为单元测试设置通用抽象基础测试类?

如果基本抽象测试类不是通用的,则标记为[TestMethod]的所有基本方法都在派生类中正确继承并在Visual Studio中执行。如果基类是泛型,则Visual Studio不会在派生类中执行这些方法。

想象一下,你有一堆实现这个接口的解析器类(简化):

// parses the input stream into an 
// instance of T
interface IParser<T> 
{
    IParserResult<T> Parse(byte[] input);
}

想象一下你有一堆可以解析某个流的解析器:

class HeaderParser : IParser<T> { ... }
class SomeOtherParser : IParser<T> { ... }
... many more ...

为了测试每个解析器的功能,可以将常见的测试模式提取到这样的抽象类中:

[TestClass]
abstract class ParserTest<T>
{
    [TestMethod]
    public void TestParser()
    {
       // 1. init parser
       var parser = new T();

       // 2. get data
       var input = GetInputData();

       // 3. parse
       var result = parser.Parse(input);

       // 4. make common assertions
       Assert.AreEqual(ParserResultType.Success, result.Type);
       Assert.AreEqual(input.Length, result.NextDataOffset);

       // 5. specific validation
       Validate(result.Value);
    }

    protected abstract byte[] GetInputData(); 

    protected abstract void Validate(T result); 
}

如果此类是通用的和抽象的,那么TestParser方法将不会作为派生类的单元测试执行。

2 个答案:

答案 0 :(得分:1)

好的,我采用了不同的方法,类似于@stijn刚才建议的方法。

我完全删除了抽象基类,并创建了一个从实际的解析器测试中调用的辅助类。这是一个更好的解决方案,因为它允许在每个派生类中使用不同的参数组合和验证方法重用相同的方法(而抽象类只有一对CreateData / TestResults抽象方法)。

public class ParserTestHelper
{
    public static void Test<T>(
         Func<IParser<T>> getParser,
         Func<byte[]> getInput,
         Action<T> checkResult)
    {
        // get parser
        var parser = getParser();

        // get input data
        var input = getInput();

        // parse
        var result = parser.Parse(input, 0);

        // common assertions
        Assert.AreEqual(ParserResultType.Success, result.ResultType);
        Assert.AreEqual(input.Length, result.NextDataOffset);

        // validate results
        checkResult(result.ParsedValue);
    }
}

派生类现在可以简单地在实际的Test方法中调用方法:

[TestClass]
public class HeaderParserTest
{
     [TestMethod]
     public void TestHeader() 
     {
         ParserTestHelper.Test(
            () => new HeaderParser(),
            () => /* generate data */,
            () => /* validate results */);
     }
}

谢谢大家!

答案 1 :(得分:0)

我同意stijns对你问题的评论。我更喜欢在一个夹具中收集所有对象的测试。此外,如果您将测试保留在抽象类中,则意味着您一遍又一遍地测试相同的功能。这至多是多余的重复;最糟糕的是让读者感到困惑。 最后,您的需求表明您的测试对象可能需要一个共同的抽象基类。如果是这种情况,则应分别测试基类,并测试每个派生类,而不测试基类的功能。

话虽这么说,我有时会使用基类进行设置和拆卸,以避免一次又一次地做同样的事情。对我来说最常见的情况是我必须在能够使用SDK之前启动许可证。我仍然没有完全相信自己,这是一个很好的方式去干。