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

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

Я хочу заглянуть в любую инфраструктуру (NUnit и т.д.) или расширение Visual Studio, чтобы выполнить это.


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

Ответ 1

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

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

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

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

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

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

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

Ответ 2

Вы можете использовать атрибуты [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] вы можете передать любое произвольное количество входных параметров, например, входные значения для тестирования или ожидаемые значения, возвращаемые вызовами метода. Вам нужно будет добавить аргументы соответствующих типов в качестве входных аргументов метода тестирования.

Ответ 3

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

Что-то вроде этого (записка - скомпилированная голова)

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

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

    ...
}

Ответ 4

Это моя конкретная реализация, основанная на ответе 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 в оба класса, иначе это не сработает. Наконец, при желании вы можете добавить к дочернему классу любые специфичные для реализации тесты (например, конструкторы).