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