Есть ли способ написать тесты для интерфейса, а затем проверить его на всех классах, которые реализуют тест? - PullRequest
1 голос
/ 21 декабря 2009

Я уже проверил это .. аналогичный вопрос но я не уверен насчет ответов ...

В настоящее время у меня есть интерфейс, который реализуется только одним классом, но он рано или поздно изменится. В настоящее время у меня есть тест на интерфейс, и все тесты в начале начинаются с:

IFoo foo = GetConcreteFoo()

где GetConcreteFoo - это что-то вроде

IFoo GetConcreteFoo()
{
   return new ConcreteFooA();
}

однако, когда я получу больше реализаций Foo, может ли быть способ сделать весь тест выполненным для списка всех различных конкретных foos?

Я думаю, что если нет способа, по крайней мере, я могу скопировать / вставить тест в новый файл с именем concreteclass и изменить возвращаемый объект GetConcreteFoo ... (и изменение исходного файла с (IFooTests на IConcreteFooATests).

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

Есть ли способ заставить это сделать это?

(я использую MSTests)

Спасибо!

Ответы [ 2 ]

3 голосов
/ 21 декабря 2009

Не уверен насчет MSTest, но я полагаю, что вы можете сделать это в NUnit, используя параметризованные тесты , например. параметризация с помощью класса реализации и использование Activator.CreateInstance для его создания.

Однако, более глубокий вопрос, вы бы хотели? Вы не говорите, как выглядит ваш интерфейс, но обычная причина наличия интерфейса - разрешение разных реализаций. Например, если у вас есть интерфейс IShape со свойством Area, это будет реализовано по-разному в Circle, Square и RorschachBlot. Что может достоверно подтвердить проверка свойства IShape.Area? Следовательно, в общем случае вы можете реально тестировать только классы и (конкретные) методы.

Конечно, если ваш интерфейс подразумевает семантические гарантии вне спецификации интерфейса (например, Area всегда больше 0), вы можете проверить это для всех реализаций, о которых вы знаете. (Для реализаций, о которых вы не знаете, когда создаете тест, вы должны полагаться на передачу этих дополнительных требований извне, например, через документацию, и доверять разработчикам выполнение. такие требования более надежно через классы контракта.)

2 голосов
/ 21 декабря 2009

Краткий ответ, да, вы можете. Более длинный ответ будет зависеть от множества факторов, влияющих на то, как он будет работать в вашем наборе тестов.

В основном вы можете сделать что-то вроде этого:

public void GenericIFooTest(IFoo testFoo) {
   // each of your tests against foo here...
   Assert.IsTrue(testFoo.DoesItsThing());
}

Затем вы можете сгенерировать свои тесты вручную:

public void TestConcreteAFoo() {
   IFoo aFoo = new ConcreteAFoo(param1, param2, param3);

   GenericIFooTest(aFoo);
}
public void TestConcreteBFoo() {
   IFoo bFoo = new ConcreteBFoo();

   GenericIFooTest(bFoo);
}

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

public void TestAllFoos() {
   foreach(string assembly in Directory.GetFiles(assemblyPath, "*.dll", SearchOptions.All) {
      Assembly currentAssembly = Assembly.LoadAssembly(assembly);

      foreach(Type internalTypes in currentAssembly.GetTypes()) {
         if (internalTypes.IsAssignableFrom(IFoo) && !(internalTypes is IFoo)) {
            IFoo fooType = AppActivator.CreateInstance(internalTypes);

            GenericIFooTest(fooType);
         }
      }
   }
}

Последняя часть из памяти какого-то кода, который я писал на прошлой неделе, чтобы сделать то же самое, немного грубовато, но вам нужно начать. Конечно, вы могли бы использовать LINQ, чтобы упростить его, я просто не знаю синтаксис на макушке.

...