在抽象泛型测试类中定义单元测试

本文关键字:定义 单元测试 测试类 抽象 泛型 | 更新日期: 2023-09-27 18:02:31

是否有可能在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方法不会作为派生类的单元测试来执行。

在抽象泛型测试类中定义单元测试

好的,我选择了另一种方法,类似于@stijn不久前建议的方法。

我完全删除了抽象基类,并创建了一个从实际解析器测试中调用的helper类。这是一个更好的解决方案,因为它允许在每个派生类中重用具有不同参数和验证方法组合的相同方法(而抽象类只有一对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);
    }
}

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

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

谢谢大家!

我同意stijns对你问题的评论。我更喜欢将一个对象的所有测试集中在一个fixture中。此外,如果您将测试保存在抽象类中,则意味着您正在反复测试相同的功能。这充其量是多余的重复;在最坏的情况下,读者会感到困惑。最后,您的需求表明您的测试对象可能需要一个公共抽象基类。如果是这种情况,您应该分别测试基类,并测试每个派生类,而不测试基类的功能。

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