Как лучше организовать наши юнит-тесты?

18

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

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

Добавьте к этому то, что нам нужно проверить. Возьмите отступ вокруг ячейки таблицы (мы используем Word, Excel и PowerPoint для дизайна отчета). Мы должны проверить заполнение по разрыву страницы, для таблицы внутри ячейки, вертикально слитых ячеек, горизонтально слитых ячеек, вертикально и горизонтально слитых ячеек, которые содержат таблицу с вертикально и горизонтально объединенными ячейками во внутренней таблице, где эта таблица ломается по странице.

Так в какую категорию входит этот тест? Заполнение таблицы, разрывы страниц, вложенные ячейки, вертикально объединенные ячейки, горизонтально объединенные ячейки или что-то еще?

И как мы документируем эти категории, называем юнит-тесты и т. Д.?

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

Например, вчера у нас был клиент, который запустил закладку Word в конце цикла forEach в своем шаблоне (документ Word) и завершил его в начале следующего цикла forEach. Это весь используемый код, который имеет модульные тесты против него, но мы не думали, что комбинация шаблона, расширяющего начало закладки, должна запускаться 25 раз, а затем заканчиваться 10 раз (два цикла forEach имели разное количество строк).

Дэвид Тилен
источник
1
Похоже, ваш вопрос на самом деле: откуда мы знаем, что мы протестировали определенный сценарий?
Энди Визендангер
Да! А также, где находятся тесты для любых похожих сценариев. И от этого мы получаем потребность № 2 - чтение того, что покрыто, помогает нам найти то, что мы пропустили.
Дэвид Тилен

Ответы:

13

Как правило, я склонен отражать исходное дерево для моих модульных тестов. Итак, если бы у меня был src / lib / fubar, у меня был бы тест / lib / fubar, который содержал бы модульные тесты для fubar.

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

Сардатрион - Восстановить Монику
источник
В настоящее время мы отражаем исходное дерево. Но у нас есть две проблемы. Во-первых, для форматирования таблиц существует более 100 различных тестов. Отслеживание того, что именно проверено, стало проблемой. Во-вторых, очень разные функциональные области должны тестировать таблицы - парсеры, подстановка данных, форматирование и создание выходного документа. Поэтому я думаю, что вы правы, в некотором смысле это функциональное тестирование данного свойства.
Дэвид Тилен
Что приводит к вопросу, где мы храним таблицу тестов? Я думаю, что электронная таблица в корневом каталоге исходных текстов ???
Дэвид Тилен
Я бы сохранил его в управляемой версией электронной таблице в тестовом каталоге. Если у вас есть много вещей, которые нужно протестировать, полезно разбить их на мета-структуры. Попробуйте подумать о том, что в целом проверяется, а не что и как.
Сардатрион - Восстановить Монику
7

Наиболее общая структура , кажется, srcи testзеркало каталога.

src/module/class
test/module/class_test

Однако есть альтернативная структура, которую я видел, и теперь я считаю, что она лучше.

src/module/class
src/module/class_test

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

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

В .NET я имею тенденцию отражать или, по крайней мере, приближать структуру пространства имен для исходного кода в тестовых проектах под основным пространством имен «Tests.Unit» или «Tests.Integration». Все модульные тесты выполняются в одном проекте, при этом базовая структура исходного кода реплицируется в виде папок в проекте. То же самое для интеграционных тестов. Итак, простое решение для проекта может выглядеть так:

Solution
   MyProduct.Project1 (Project)
      Folder1 (Folder)
         ClassAA (Class def)
         ...
      Folder2
         ClassAB
         ...
      ClassAC
      ...
   MyProduct.Project2
      Folder1
         ClassBA
         ...
      ClassBB
      ...
   ...
   MyProduct.Tests.Unit
      Project1
         Folder1
            ClassAATests
            ClassAATests2 (possibly a different fixture setup)
         Folder2
            ClassABTests
         ClassACTests
      Project2
         Folder1
            ClassBATests
         ClassBBTests
      ...
   MyProduct.Tests.Integration
      Project1 (a folder named similarly to the project)
         Folder1 (replicate the folders/namespaces for that project beneath)
            ClassAATests
         Folder2
            ClassABTests
         ClassACTests
      Project2
         Folder1
            ClassBATests
         ClassBBTests

Для любых AAT или AEET, которые закодированы в рамках модульного тестирования, это немного меняется; обычно эти тесты отражают ряд шагов, которые проверят функциональность нового варианта использования или истории. Я обычно структурирую эти тесты в MyProduct.Tests.Acceptanceпроекте как таковом, с тестами для каждой истории, возможно, сгруппированными по вехам или «эпическим» историям, к которым принадлежала разрабатываемая история. Однако на самом деле это просто тесты uber-интеграции, и поэтому, если вы предпочитаете структурировать тесты более объектно-ориентированным, а не сюжетно-ориентированным способом, вам даже не понадобится MyProduct.Tests.Acceptanceаналогичный проект; просто добавьте их в MyProduct.Tests.Integrationобласть действия объекта самого высокого уровня.

Keiths
источник
3

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

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

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

Что касается именования, то нет особой ценности в затратах усилий на имена модульных тестов. Все, что вы хотите услышать от ваших тестов, это «5235 из 5235 тестов пройдено». Когда тест не пройден, вы читаете не его имя, а сообщение , например, строку в assert()которой реализует ваш критерий успеха. Сообщение должно быть достаточно информативным, чтобы вы имели представление о том, что не так, не читая текст теста. Имя неважно по сравнению с этим.

Килиан Фот
источник
Согласитесь 100% на все, что вы говорите (наша сборочная машина выполняет все тесты при регистрации). Нашей большой проблемой является отслеживание того, что мы тестируем. И покрытие кода не очень помогает (см. Обновление выше).
Дэвид Тилен
1

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

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

mouviciel
источник