В своем выступлении TDD, где все это пошло не так , Ян Купер выдвигает первоначальное намерение Кента Бека за модульное тестирование в TDD (для тестирования поведения, а не методов классов в частности) и высказывается за недопущение связи тестов с реализацией.
В случае поведения, подобного save X to some data source
системе с типичным набором сервисов и репозиториев, как мы можем провести единичное тестирование сохранения некоторых данных на уровне сервиса через репозиторий, не привязывая тест к деталям реализации (например, вызывая определенный метод? )? Не стоит ли избегать такого рода связей на самом деле?
unit-testing
tdd
coupling
Энди Хант
источник
источник
Ответы:
Ваш конкретный пример - это случай, который вы обычно должны проверять, проверяя, был ли вызван определенный метод, потому что
saving X to data source
означает взаимодействие с внешней зависимостью , поэтому вы должны проверить, что общение происходит так, как ожидалось .Тем не менее, это не плохо. Граничные интерфейсы между вашим приложением и его внешними зависимостями не являются деталями реализации , на самом деле они определены в архитектуре вашей системы; Это означает, что такая граница вряд ли изменится (или, если это необходимо, это будет наименее частый вид изменений). Таким образом, связывание ваших тестов с
repository
интерфейсом не должно доставлять вам слишком много хлопот (если это так, подумайте, не нарушает ли интерфейс ответственность из приложения).Теперь рассмотрим только бизнес-правила приложения, отделенного от пользовательского интерфейса, баз данных и других внешних служб. Это где вы должны быть свободны, чтобы изменить как структуру, так и поведение кода. Именно здесь сочетание тестов и деталей реализации заставит вас изменить больше тестового кода, чем производственного кода, даже если нет общего изменения в поведении приложения. Это где тестирование,
State
а неInteraction
помогает нам идти быстрее.PS: я не собираюсь говорить, является ли тестирование государственным или интерактивным оборудованием единственно верным способом TDD - я считаю, что это вопрос использования правильного инструмента для правильной работы.
источник
Моя интерпретация этого разговора такова:
Об этом не говорится в разговоре, но я думаю, что предполагаемый контекст для совета выглядит примерно так:
Таким образом, тестирование компонента является максимально возможной областью, в которой что-то еще можно разумно назвать модульным тестированием. Это довольно отличается от того, как некоторые люди, особенно ученые, используют этот термин. Это не что иное, как примеры в типичном учебном пособии по модульному тестированию. Это, однако, соответствует своему происхождению в тестировании оборудования; Платы и модули тестируются модулем, а не проводами и винтами. Или, по крайней мере, вы не строите фиктивный Боинг для проверки винта ...
Экстраполируя это и добавляя некоторые свои мысли,
Если вы делаете это правильно и чисто, вам едва нужен насмешливый инструмент; он используется только несколько раз для каждой системы.
База данных, как правило, является соавтором, поэтому она притворяется, а не издевается. Это было бы больно осуществлять вручную; К счастью, такие вещи уже существуют .
Базовым тестовым шаблоном является выполнение некоторой последовательности операций (например, сохранение и перезагрузка документа); подтвердите, что это работает. Это то же самое, что и для любого другого тестового сценария; никакое (работающее) изменение реализации, скорее всего, не приведет к сбою такого теста.
Исключение составляют записи базы данных, которые никогда не читаются тестируемой системой; например, журналы аудита или аналогичные. Это выходы, и поэтому должны быть осмеяны. Тестовым шаблоном является выполнение некоторой последовательности операций; подтвердите, что интерфейс аудита был вызван с указанными методами и аргументами.
Обратите внимание, что даже здесь, при условии, что вы используете безопасный для типов инструмент для пересмешивания, такой как mockito , переименование метода интерфейса не может вызвать сбой теста. Если вы используете IDE с загруженными тестами, она будет реорганизована вместе с методом переименования. Если вы этого не сделаете, тест не будет компилироваться.
источник
Мое предложение состоит в том, чтобы использовать основанный на состоянии подход тестирования:
ДАЛИ У нас есть тестовая БД в известном состоянии
КОГДА служба вызывается с аргументами X
ПОТОМ утверждаем, что БД перешла из исходного состояния в ожидаемое состояние, вызвав методы репозитория только для чтения и проверив их возвращаемые значения
Поступая таким образом, вы не полагаетесь на какой-либо внутренний алгоритм сервиса и можете реорганизовать его реализацию без необходимости менять тесты.
Единственное соединение здесь - это вызов метода сервиса и вызовы репозитория, необходимые для чтения данных из БД, что нормально.
источник