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

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

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


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

Ответы [ 4 ]

6 голосов
/ 21 февраля 2012

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

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

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

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

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

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

Ссылки: шаблон Binder's Polymorphic Server и McGregor PACT - параллельная архитектура для тестирования компонентов

2 голосов
/ 21 февраля 2012

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

2 голосов
/ 21 февраля 2012

Расширяя ответ Джо , вы можете использовать атрибут [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;

    ...
}
2 голосов
/ 21 февраля 2012

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

...