Могу ли я реализовать серию повторно используемых тестов для проверки реализации интерфейса?

Я пишу серию классов коллекции в C#, каждый из которых реализует аналогичные пользовательские интерфейсы. Можно ли написать единую коллекцию модульных тестов для интерфейса и автоматически запустить их все в нескольких различных реализациях? Я хотел бы избежать дублирования кода тестирования для каждой реализации.

Я хочу изучить любой фреймворк (NUnit и т. Д.) Или расширение Visual Studio для достижения этой цели.


Для тех, кто хочет сделать то же самое, в качестве ответа я опубликовал свое конкретное решение, основанное на принятом решении avandeursen.

4 ответа

Решение

Да, это возможно. Хитрость заключается в том, чтобы позволить вашей иерархии тестирования модульного класса следовать иерархии классов вашего кода.

Давайте предположим, что у вас есть интерфейс Itf с реализацией классов C1 а также C2,

Вы сначала создаете тестовый класс для Itf (ItfTest). Чтобы на самом деле выполнить тест, вам нужно создать фиктивную реализацию вашего Itf интерфейс.

Все тесты в этом ItfTest следует передать любую реализацию Itf (!). Если нет, ваша реализация не соответствует принципу подстановки Лискова (буква "L" в твердых принципах Мартина ОО-дизайна)

Таким образом, чтобы создать контрольный пример для C1, ваш C1Test класс может расширяться ItfTest, Ваше расширение должно заменить создание фиктивного объекта созданием C1 объект (внедрение или использование фабричного метода GoF). Таким образом, все ItfTest случаи применяются к экземплярам типа C1, Кроме того, ваш C1Test класс может содержать дополнительные тестовые примеры, специфичные для C1,

Аналогично для C2, И вы можете повторить трюк для более глубоких вложенных классов и интерфейсов.

Ссылки: Полиморфный тестовый шаблон Binder и PACT Макгрегора - Параллельная архитектура для тестирования компонентов.

Это моя конкретная реализация, основанная на ответе avandeursen:

[TestClass]
public abstract class IMyInterfaceTests
{
    protected abstract IMyInterface CreateInstance();

    [TestMethod]
    public void SomeTest()
    {
        IMyInterface instance = CreateInstance();
        // Run the test
    }
}

Каждая реализация интерфейса затем определяет следующий тестовый класс:

[TestClass]
public class MyImplementationTests : IMyInterfaceTests
{
    protected override IMyInterface CreateInstance()
    {
        return new MyImplementation();
    }
}

SomeTest запускается один раз для каждого конкретного TestClass происходит от IMyInterfaceTests, Используя абстрактный базовый класс, я избегаю необходимости каких-либо ложных реализаций. Обязательно добавлю TestClassAttribute в оба класса, или это не сработает. Наконец, вы можете добавить любые специфичные для реализации тесты (например, конструкторы) в дочерний класс, если это необходимо.

Расширяя ответ Joe Alfano, вы можете использовать атрибут [TestCaseSource] в NUnit аналогично RowTest MBUnit. Вы можете создать исходный код для теста с именами ваших классов. Затем вы можете украсить каждый тест, который атрибут TestCaseSource. Затем с помощью Activator.CreateInstance вы можете привести к интерфейсу, и вы будете настроены.

Как то так (примечание - голова скомпилирована)

string[] MyClassNameList = { "Class1", "Class2" };

[TestCaseSource(MyClassNameList)]
public void Test1(string className)
{
    var instance = Activator.CreateInstance(Type.FromName(className)) as IMyInterface;

    ...
}

Для этого вы можете использовать атрибуты [RowTest] в MBUnit. В приведенном ниже примере показано, где вы передаете методу строку, указывающую, какой класс реализации интерфейса вы хотите создать, а затем создаете этот класс с помощью отражения:

[RowTest]
[Row("Class1")]
[Row("Class2")]
[Row("Class3")]
public void TestMethod(string type)
{
   IMyInterface foo = Activator.CreateInstance(Type.GetType(type)) as IMyInterface;

   //Do tests on foo:
}

В атрибутах [Row] вы можете передать любое произвольное количество входных параметров, таких как входные значения для тестирования или ожидаемые значения, которые будут возвращены вызовами метода. Вам нужно будет добавить аргументы соответствующих типов в качестве входных аргументов метода тестирования.

Другие вопросы по тегам