Определение модульных тестов в абстрактных универсальных тестовых классах - PullRequest
2 голосов
/ 15 июля 2011

Возможно ли иметь базовый класс общего теста для базовых тестов в 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 ]

1 голос
/ 15 июля 2011

Хорошо, я выбрал другой подход, аналогичный тому, что @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 */);
     }
}

Спасибо всем!

0 голосов
/ 15 июля 2011

Я согласен с комментариями Стийнса по вашему вопросу. Я предпочитаю, чтобы все тестирование объекта собиралось в одном приборе. Кроме того, если вы держите тесты в абстрактном классе, это означает, что вы тестируете одну и ту же функциональность снова и снова. Это в лучшем случае избыточное повторение; в худшем случае сбивает с толку читателя. Наконец, ваши потребности указывают, что ваши тестируемые объекты могут нуждаться в общем абстрактном базовом классе. Если это так, вам следует протестировать базовый класс отдельно и протестировать каждый из производных классов без проверки функциональности базового класса.

При этом я иногда использовал базовые классы для настройки и демонтажа, чтобы не делать одно и то же снова и снова. Самая распространенная ситуация для меня - это когда мне нужно инициировать лицензии, прежде чем я смогу использовать SDK. Я до сих пор не совсем убедил себя в том, что это хороший способ сделать СУХОЙ.

...