Сколько насмешек «просто правильно»?

10

Я назвал вопрос в шутку, потому что уверен, что «это зависит», но у меня есть некоторые конкретные вопросы.

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

Поэтому я был удивлен, что Рой Ошеров предложил в этом видео, что вы должны использовать насмешку только в 5% случаев. Я предполагаю, что мы сидим где-то между 70-90%. Время от времени я видел и другие подобные указания .

Я должен определить то, что я считаю двумя категориями «интеграционных тестов», которые настолько различны, что им действительно нужно дать разные названия: 1) внутрипроцессные тесты, которые объединяют несколько модулей кода, и 2) внепроцессные тесты, которые говорят к базам данных, файловым системам, веб-службам и т. д. Меня интересует тип № 1 - тесты, которые интегрируют несколько модулей кода в процессе работы.

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

Учитывая это, представляется необходимым довольно часто использовать насмешки для изоляции этих отдельных блоков кода.

Дана объектная модель следующим образом:

введите описание изображения здесь

... Также учтите, что глубина зависимости нашего приложения намного глубже, чем я мог бы уместить на этом изображении, так что между слоями 2-4 и 5-13 есть несколько слоев N.

Если я хочу проверить какое-то простое логическое решение, принимаемое в модуле № 1, и если каждая зависимость вводится конструктором в модуль кода, который зависит от него, так что, скажем, 2, 3 и 4 являются конструктором, вводимым в модуль 1 в изображение, я бы предпочел ввести макеты 2, 3 и 4 в 1.

В противном случае мне нужно будет создать конкретные экземпляры 2, 3 и 4. Это может быть более сложным, чем просто дополнительная печать. Часто 2, 3 и 4 будут иметь требования к конструктору, которые могут быть сложными для удовлетворения, и в соответствии с графиком (и в соответствии с реальностью нашего проекта), мне нужно будет построить конкретные экземпляры с N по 13, чтобы удовлетворить конструкторы 2, 3 и 4.

Эта ситуация становится более сложной, когда мне нужно 2, 3 или 4, чтобы вести себя определенным образом, чтобы я мог проверить простое логическое решение в # 1. Мне может понадобиться понять и «мысленно рассуждать» обо всем графе / дереве объектов сразу, чтобы заставить 2, 3 или 4 вести себя в нужном направлении. Часто кажется, что намного проще сделать myMockOfModule2.Setup (x => x.GoLeftOrRight ()). Returns (new Right ()); чтобы проверить, что модуль 1 отвечает, как ожидалось, когда модуль 2 говорит ему, чтобы он пошел правильно.

Если бы я тестировал конкретные экземпляры 2 ... N ... 13 вместе, установки теста были бы очень большими и в основном дублировались. Неудачные тесты могут не очень хорошо определить местонахождение неудач регрессий. Тесты не будут независимыми ( еще одна вспомогательная ссылка ).

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

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

ardave
источник
Мне кажется, что ваше приложение может выиграть от более слабой связи. en.wikipedia.org/wiki/Loose_coupling
Роберт Харви,
1
Or is there perhaps some assumption in typical unit testing guidance that the layers of dependency in most applications will be shallow enough that it is indeed reasonable to test all of the code modules integrated together (making our case "special")? <- Это.
Роберт Харви
Также стоит отметить: цель регрессионных тестов (особенно интеграционного тестирования) - доказать, что ваше программное обеспечение все еще работает, а не обязательно определять, где оно ломается. Вы можете сделать это с помощью устранения неполадок, исправить проблему, а затем покрыть конкретную поломку с помощью дополнительных модульных тестов.
Роберт Харви
Я должен был быть более ясным в оригинальном посте, чтобы сказать, что модуль 1 знает только о I2, I3 и I4. Модуль 2 знает только о I5, I6 и I7. Это только сомнительная цель тестирования без использования насмешек, которые я бы поставил конкретные 2, 3 и 4 к 1, что приведет к проблемам, которые я описал. В противном случае мы используем макеты гораздо чаще, чем в 5% случаев.
ardave
Я как-то пошутил, что наш случай «особенный» после прочтения поста в блоге о том, что многие команды игнорируют ценные соглашения, потому что они неправильно считают, что их ситуация «особенная». Но если это действительно наш случай, это объяснило бы несоответствие между некоторыми руководящими принципами сообщества, которые я прочитал, и некоторым из фактического опыта моей команды. (5% против 70-90%)
ardave

Ответы:

4

Наша команда злоупотребляет издевательствами?

Не на первый взгляд.

Если у вас есть модули 1..13, то у каждого должны быть свои собственные модульные тесты, и все (нетривиальные, ненадежные) зависимости должны быть заменены тестовыми версиями. Это может означать имитации, но некоторые люди педантичны с именами, так что подделки, прокладки, нулевые объекты ... некоторые люди называют все тестовые реализации "ложными". Это может быть источником путаницы о том, сколько «правильно».

Лично я просто называю все тестовые объекты «фиктивными», потому что различать их разнообразие не часто полезно. Пока они держат мои юнит-тесты быстрыми, изолированными и устойчивыми ... Мне все равно, как они называются.

Telastyn
источник
Тогда я хотел бы спросить, есть ли какое- либо общее руководство для того, когда лучше всего тестировать модули кода изолированно, а не тестировать более одного модуля кода, объединенного вместе. Кажется, что, как только я интегрирую любые два модуля, которые я мог бы изолировать, я открываю себя для множества нежелательных проблем: отсутствие точного определения причин регрессии / нескольких тестов для одной регрессии, чрезмерных настроек теста и т. Д. У меня есть собственное интуитивное чувство («прислушиваться к тестам»), но это оставило меня на уровне имитации 70-90%.
ardave
1
@nono - По моему опыту, вы должны тестировать все по отдельности по причинам, которые вы упомянули. Единственное, что вы не тестируете модульно, - это то, что вы не можете, потому что они напрямую связаны с какой-либо файловой системой или другим внешним ресурсом ( что-то должно делать это в конце концов).
Теластин
После того, как вы пережевали это в течение нескольких дней, ваше объяснение кажется наилучшим из возможных: если бы нужно было использовать строгое определение «макет» как тип двойного теста, используемого для ретроспективной проверки взаимодействия / поведения, в противоположность фиктивному двойному тесту или двойной тест, который настроен заранее, чтобы имитировать определенное поведение, тогда да, я мог видеть завершение на уровне 5%.
Ardave