Defining unit tests in abstract generic test classes
Is it possible to have a generic abstract base test class for unit tests in Visual Studio 2008?
If the base abstract test class is not generic, all its base methods marked with [TestMethod]
are properly inherited in derived classes and executed in Visual Studio. If the base class is generic, then 开发者_如何学CVisual Studio doesn't execute those methods in derived classes.
Imagine you have a bunch of parser classes implementing this interface (simplified):
// parses the input stream into an
// instance of T
interface IParser<T>
{
IParserResult<T> Parse(byte[] input);
}
And imagine you have a bunch of parsers which can parse a certain stream:
class HeaderParser : IParser<T> { ... }
class SomeOtherParser : IParser<T> { ... }
... many more ...
To test the functionality of each parser, common testing pattern might be extracted into an abstract class like this:
[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);
}
If this class is generic and abstract, then the TestParser
method won't be executed as a unit test for derived classes.
Ok, I went for a different approach, similar to what @stijn suggested a while ago.
I removed the abstract base class completely, and created a helper class which gets called from actual parser tests. This is a much better solution, because it allows reusing the same method with different combination of parameters and validation methods in each derived class (while abstract class only had a single pair of CreateData/TestResults abstract methods).
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);
}
}
And derived classes can now simply call the method inside actual Test methods:
[TestClass]
public class HeaderParserTest
{
[TestMethod]
public void TestHeader()
{
ParserTestHelper.Test(
() => new HeaderParser(),
() => /* generate data */,
() => /* validate results */);
}
}
Thanks everyone!
I agree with stijns comments on your question. I prefer all testing of an object is gathered in one fixture. Further, if you keep tests in an abstract class, it means that your are testing the same functionality over and over again. This is at best redundant repeating; at worst confusing to the reader. Finally , your needs indicate that your tested objects may be in need of a common abstract base class. If this is the case, you should test the base class separately, and test each of the derived classes without testing the functionality of the base class.
That being said, I have sometimes used base classes for setup and teardown to avoid doing the same thing again and again. The most common situation for me is when I have to initiate licenses before being able to use a SDK. I have still not entirely conviced myself that this is a pretty way to go DRY though.
精彩评论