Необходимо ли модульное тестирование определения интерфейса? - PullRequest
11 голосов
/ 17 марта 2010

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

Рассмотрим пример с ультрамалым и необычным названием:

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);
   }
}

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

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

Ответы [ 7 ]

24 голосов
/ 17 марта 2010

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

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

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

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

13 голосов
/ 17 марта 2010

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

3 голосов
/ 17 марта 2010

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

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

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

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

2 голосов
/ 17 марта 2010

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

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

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

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

1 голос
/ 17 марта 2010

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

0 голосов
/ 20 марта 2010

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

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

0 голосов
/ 19 марта 2010

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...