Требуется ли модульное тестирование определения интерфейса?

Я иногда слышал или читал о людях, утверждающих их интерфейсы в unit test. Я не имею в виду насмешку над интерфейсом для использования в тесте другого типа, но специально создавая тест для сопровождения интерфейса.

Рассмотрим этот ультра-хромой и пример с манжетой:

public interface IDoSomething
{
   string DoSomething();
}

и тест:

[TestFixture]
public class IDoSomethingTests
{
   [Test]
   public void DoSomething_Should_Return_Value()
   {
        var mock = new Mock<IDoSomething>();
        var actualValue = mock.Expect(m => m.DoSomething()).Returns("value");

        mock.Object.DoSomething();
        mock.Verify(m => DoSomething());
        Assert.AreEqual("value", actualValue);
   }
}

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

Это обычная (рекомендуемая) практика?

Ответ 1

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

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

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

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

Ответ 2

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

Ответ 3

Интерфейсы связаны с хорошо разработанными контрактами, а не с хорошо реализованными. Поскольку С# не является динамическим языком, который позволил бы интерфейсу не реализовываться во время выполнения, такой тип теста не подходит для языка. Если бы это был Ruby или Perl, тогда, может быть...

Контракт - это идея. Звучность идеи - это то, что требует тщательного изучения человека во время разработки, а не во время выполнения или времени тестирования.

Реализация может быть "функциональным" набором пустых заглушек. Это все равно пройдет тест "Интерфейс", но будет плохой выполнение контракта. Это все еще не означает, что контракт плох.

О всех конкретных тестах интерфейса - это напоминание о первоначальном намерении, которое просто требует, чтобы вы изменили код в 2-х местах, когда ваши намерения изменились.

Ответ 4

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

public interface ArrayMangler
{
    void SetArray (Array myArray);
    Array GetSortedArray ();
    Array GetReverseSortedArray();
}

Вы можете написать общие тесты для ArrayMangler и убедиться, что массивы, возвращаемые GetSortedArray, действительно отсортированы, и GetReverseSortedArray действительно отсортированы в обратном порядке.

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

Ответ 5

По-моему, это не путь. Интерфейс создается как акт рефакторинга (интерфейс извлечения), а не TDD. Итак, вы начинаете с создания класса с TDD, и после этого вы извлекаете интерфейс (при необходимости).

Ответ 6

Сам компилятор проверяет интерфейс. TDD выполняет проверку интерфейса.

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

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

TDD проверяет интерфейс, проверяя, что охват достигается без использования конкретных типов (конкретных, которые реализуют интерфейс).

Надеюсь, это поможет.

Ответ 7

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

Если вы не возражаете против рекламы, то в нашей книге больше http://www.growing-object-oriented-software.com/