Какая сигнатура должна быть у функции теста
Перейти к содержимому

Какая сигнатура должна быть у функции теста

Анатомия юнит тестирования

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

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

Я определяю юнит тестирования как тестирование одного продакш юнита в полностью контролируемом окружении.

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

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

О наследование

Постарайтесь не применять наследование. Вместо него используйте композицию зависимостей. Часто наследование применяют для реализации принципа DRY (don’t repeat yourself), вынося общий код в родителя, но тем самым нарушая принцип KISS (keep it simple stupid), увеличивая сложность юнитов.

AAA (Arrange, Act, Assert) паттерн

Если посмотреть на юнит тест, то для большинства можно четко выделить 3 части кода:

Arrange (настройка) — в этом блоке кода мы настраиваем тестовое окружение тестируемого юнита;
Act — выполнение или вызов тестируемого сценария;
Assert — проверка того, что тестируемый вызов ведет себя определенным образом.
Этот паттерн улучшает структуру кода и его читабельность, однако начинать писать тест нужно всегда с элемента Act.

Driven approach

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

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

С чего мы начинаем разработку конкретного функционала? — с требований бизнеса, которые типично выглядят так: “Пользователь с любой ролью должен иметь возможность создать запись, таким образом, он выполнит такую то бизнес операцию”.

Используя driven approach первое что мы должны сделать —

  • Это создать место в UI слое, где пользователь может создать запись, скажем, страницу в приложении, на которой будет кнопка “Создать запись”. Почему мы это сделали? — потому что это требует бизнес история.
  • Кнопка “Создать запись” будет требовать реализации обработчика click события.
  • Обработчик события будет требовать реализации создания записи в терминах слоя бизнес логики.
  • В случае клиент-серверной архитектуры, клиент будет обращаться к некоторому end point на стороне сервера для создания этой записи.
  • Сервер, в свою очередь, может работать с базой данных, где такая запись должна быть создана в отдельной таблице.

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

AAS (Act, Assert, Setup) паттерн

AAS — этот тот же AAA паттерн, но с измененным порядком частей, отсортированных с учетом Driven approach и переименованной Arrange частью в Setup, чтобы отличать их по названию.

Первое, что мы делаем, при создании теста — мы создаем Act. Обычно это создание экземпляра класса тестируемого юнита и вызов его функции. С одной стороны — это самый простой шаг, а с другой это то, что диктует нам бизнес история.

Второе — мы проверяем что Act действует ожидаемо. Мы пишем Assert часть, где выражаем требуемые последствия Act, в том числе с точки зрения бизнес истории.

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

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

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

Принципы SOLID

Из принципа SOLID, с точки зрения юнит тестирования очень важны 2 принципа:

Single responsibility principle — позволяет снизить количество тест кейсов для юнита. В среднем на юнит должно приходиться от 1 до 9 тест кейсов. Это очень хороший индикатор качества юнита — если тест кейсов больше или хочется их сгруппировать, то вам точно нужно разделить его на два и больше независимых юнитов.

Dependency inversion principle — позволяет легко создавать и управлять сложнейшими окружениями для тестирования через IoC контейнеры. В соответствии с данным принципом, юнит должен зависеть от абстракций, что позволяет передавать ему любые реализации его зависимостей. В том числе, и не продакшен реализации, созданные специально для его тестирования. Эти реализации не имеют в себе никакой бизнес логики и созданы не только под конкретный тестируемый юнит, но и под конкретный сценарий его тестирования. Обычно они создаются с помощью одной из библиотек для mock объектов, такой как moq.

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

Качество кода

Кстати, несколько слов о качестве кода тестов и продакшн. Самым качественным кодом должен быть код тестов. Причина этому одна — это его размер. На 1 строку продакшн кода в среднем приходиться 2-3 строки тестового кода, то есть его в 2-3 раза больше чем продакшн кода. В этих условиях он должен хорошо читаться, быть структурированным, иметь хорошую типизацию и быть очень дружелюбным к инструментам автоматического рефакторинга. Это цели, которые достойны отдельных мероприятий и усилий.

Однотипность тестирования

Много приложения реализовано в распределенной и модульной архитектуре, где разные части написаны на различных языках, скажем, клиент-серверные приложения, где клиент написан под веб на typescript и сервер написанный на c#. Важной целью для таких проектов будет приведение тестов для любой части, независимо от языка к единому подходу. Это значит, что все тесты на проекте используют AAA или AAS подход. Все тесты используют mock библиотеки с похожим API. Все тесты используют IoC. И все тесты используют одинаковые метафоры. Это позволяет повысить переносимость удачных практик на разные части проекта, упростить адаптацию новых коллег (выучил раз и применяй везде).

Количество тестов для одного продакшн юнита

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

Моя команда создает клиент-серверные приложения, где мы используем angular на клиенте и .net core для серверной части. В следующей статье я хочу показать на примерах, как мы пишем юнит тесты под angular и с#. Как мы делаем их похожими, как располагаем в проектах, какие библиотеки применяем.

  • Тестирование IT-систем
  • Управление разработкой

JavaScript: Сигнатура функции

Функция Math.pow() , возводящая число в какую-нибудь степень, принимает два параметра: какое число возводить и в какую степень возводить. Если вызывать pow() без параметров, то вернется NaN . Функция честно пытается выполнить возведение в степень, но если значение не передано, то интерпретатор автоматически передает ей undefined . JavaScript заставляет программистов быть более аккуратным, чем остальные языки. В большинстве языков, если передать в функцию меньше параметров, чем она ожидает, то возникнет ошибка, — но только не в JavaScript. NaN вернется и при передаче любых не числовых значений:

const result = Math.pow(2, 'boom'); console.log(result); // => NaN 

Другая функция может иметь другое число параметров и другие типы параметров. Например, может существовать функция, которая принимает три параметра: число, строку и ещё одно число.

Откуда мы знаем, сколько каких параметров нужно функции Math.pow() и какого типа будет «возврат»? Мы заглянули в сигнатуру этой функции. Сигнатура определяет входные параметры и их типы, а также выходной параметр и его тип. Про функцию Math.pow() можно почитать в документации. В разделе «Синтаксис» есть такой текст:

Math.pow(base, exponent) Параметры base Основание степени. exponent Показатель степени, в которую возводится основание base. 

Это сигнатура функции и короткое пояснение на русском языке. Документация позволяет понять, сколько аргументов у функции и какого они типа, возвращает ли что-то функция и если да, то какого типа возвращаемое значение.

Задание

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

В Math есть функция ceil() . Изучите её документацию.

Напишите программу, которая использует функцию Math.ceil() с константой number и выводит результат на экран.

Упражнение не проходит проверку — что делать? ��

Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:

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

В моей среде код работает, а здесь нет ��

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Мой код отличается от решения учителя ��

Это нормально ��, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.

В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Прочитал урок — ничего не понятно ��

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

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

Полезное

Определения

  • Сигнатура функции — формальное описание типов параметров и типа возвращаемого значения функции.

7 правил хорошего тона при написании Unit-тестов

Привет, коллеги! Сегодня я бы хотел поговорить о Unit-тестировании и некоторых “правилах” при их написании. Конечно, они неформальные и не обязательны к выполнению, но при их соблюдении всем будет приятно и легко читать и поддерживать тесты, которые вы написали. Мы в Wrike видели достаточно Unit-тестов, чтобы понять основные проблемы, которые возникают при их написании и поддержке, и сформулировать несколько правил для их предотвращения.

1. Unit-тесты нужно писать. Да, как бы банально это не звучало, но писать их нужно. Каждый кусок логики приложения должен быть протестирован, чтобы в будущем избежать проблем. А они могут возникнуть при изменении логики, рефакторинге, или даже при обновлении версии зависимых библиотек. И чем больше покрытие кода тестами, тем быстрее проблема будет обнаружена и исправлена.

image

2. Это правило очень актуально для тех, кого заставляют покрывать код тестами, и оно звучит так: Тесты — это тоже код, и относиться к нему нужно как к рабочему коду. Это касается и нейминга переменных, и форматирования кода внутри теста, и, особенно, названий тестовых методов. Конечно, написание адекватного имени переменной занимает немного больше времени и ударов по клавиатуре, чем “int i = 0;”, но это повышает читабельность тестов и легкость их поддержки.
Угадайте, что проверяет упавший тестовый метод?)

3. Третье правило

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

И даже не потому, что тебя зовут не Andrey, а потому что у тебя мак. И как же быть в такой ситуации, спросите вы? Ответ прост — Относительные пути. Вот пример —

Лучше всего использовать Unix разделитель (/). Это и гораздо лаконичнее, и меньше шансов получить непредвиденную ошибку.

4. Чаще используйте заглушки (моки) вместо реальных объектов. Моки — это здорово! Ими можно управлять так, как нужно в конкретном тесте. Но, конечно, не стоит забывать сбрасывать состояние заглушек перед каждым тестовым методом. Использование заглушек повышает автономность теста и его гибкость. Не нужно подгонять состояние системы для конкретного случая, а просто настроил заглушку на возвращение нужного значения при вызове определенного метода и все. Хочется проверить другую ситуацию — исправил возвращаемое значение на другое. Легко и просто. И самое главное, что состояние всей системы при этом не изменяется — она ничего не записывает на диск, не передает по сети, не пересчитывает массивы данных, не лезет в другие сервисы. Просто заглушка и возвращаемое значение.
Для использования заглушек в тестах я использую фреймворк Mockito. С его помощью создавать заглушки очень просто. Вот например:

Здесь создается мок объекта calendar и передается в объект calendarService. Далее моки инициализируются в методе setUp. Затем непосредственно внутри теста мок настраивается и тест проверяет isModern, если тип календаря разный или не задан вовсе. При этом не пришлось пересоздавать CalendarService, а создание моков и генерация возвращаемых значений заняло всего несколько строк.

5. Пишите осмысленные сообщения на случай падения теста. Самое часто встречающееся сообщение, которое я видел, разбирая упавшие тесты на TeamCity — это

Ну сразу же все понятно! Но бывает, что сообщение об ошибке все-таки есть, но пользы от него…

А вот уже хорошее, но еще не идеальное, сообщение, в котором сразу описано, что проверялось

Идеальным можно считать сообщение, которое не только показывает что мы проверяем, но и почему мы это ожидаем

Но здесь показана достаточно простая проверка, а если нужно сравнить пару массивов с данными в разном порядке? Какое сообщение нужно написать здесь? Для этого я советую воспользоваться фреймворком AssertJ. По ссылке много простых и понятных примеров использования, после которых вам захочется воспользоваться этим фреймворком! AssertJ позволяет меньше задумываться о написании сообщения в случае ошибки, а также проверить кучу всего одной строкой, экономя место. Например проверка:

Выдаст нам замечательное сообщение об ошибке:

И все понятно — что случилось и по какой причине! Можно идти и исправлять ошибки.
И еще раз — Адекватные сообщения в ошибках тестов экономят время и нервы тех, кто будет эти тесты разбирать. Возможно, это будете вы сами через год.

6. Убирайте за собой мусор (нет, это не про запуск GarbageCollector-a). Создали файл, сделали запись в базу или дернули ручку создания пользователя? Не поленитесь и почистите за собой после теста. Файлы копятся, база обрастает кучей мусора и в системе появляются толпы фейковых пользователей. Старайтесь сохраняйте в чистоте не только своё рабочее место, но и рабочее окружение. UPD Как правильно указали в комментариях, этот пункт относится только к интеграционному тестированию.

7. Проверьте, что тест запускается где-то еще, помимо вашей локальной машины. Если у вас есть сервер CI или какое-то другое место, где вы прогоняете тесты, проверьте, что тест запустился и там. Например, тесты на сервере CI запускаются из определенного пакета, а вы положили свой в другой пакет. Или тесты запускаются по определенному имени, например *UTest, а вы назвали свой класс TestUid. Или тесты запускаются по группам, а вы забыли проставить определенную группу для своего теста. Или… Можно придумать много случаев, когда свеженаписанный тест так ниразу и не запустится где-то кроме вашей локальной машины. И тогда пользы от него не так уж и много!

В результате получился небольшой список правил хорошего тона при написании Unit-тестов. Надеюсь эта статья поможет ещё немного поднять культуру тестов и тестирования в вашей команде. Если вам есть чем дополнить мой список, добро пожаловать в комментарии.

Тестирование библиотеки DLL C++

Область применения:yesVisual Studio Visual Studio для Mac noVisual Studio Code no

В этом разделе описывается один способ создания модульных тестов для библиотеки DLL на C++ для приложений на универсальной платформе Windows (UWP) с использованием среды тестирования Майкрософт для C++. Библиотека DLL RooterLib демонстрирует концепции теории пределов из математического анализа за счет реализации функции, которая вычисляет оценку квадратного корня из заданного числа. Библиотеки DLL могут быть включены в приложение UWP, демонстрирующее пользователям интересные вещи, которые можно сделать с помощью математических функций.

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

В этом разделе также создается одно решение Visual Studio и отдельные проекты для модульных тестов и для тестируемой библиотеки DLL. Модульные тесты можно включить непосредственно в проект библиотеки DLL или создать отдельные решения для модульных тестов и для библиотеки DLL. Рекомендации по выбору структуры см. в разделе Добавление модульных тестов в существующие приложения C++.

Создание решения и проекта модульного теста

Начнем с создания нового тестового проекта. В меню Файл последовательно выберите пункты Создатьи >Проект. В диалоговом окне Создание нового проекта в поле поиска введите «тест» и задайте Язык как C++. В списке шаблонов проектов выберите Приложение модульного тестирования (универсальное приложение Windows).

Create a new UWP test project

  1. В диалоговом окне «Новый проект» разверните узел Установленные>Visual C++ и выберите Универсальные приложения Windows. В списке шаблонов проектов выберите Приложение модульного тестирования (универсальное приложение Windows).
  2. Назовите проект RooterLibTests , укажите расположение, назовите решение RooterLib , установите флажок Создать каталог для решения. Specify the solution and project name and location
  3. В новом проекте откройте файл unittest1.cpp. unittest1.cppОбратите внимание на следующие условия.
    • Каждый тест определяется с использованием TEST_METHOD(YourTestName) <. >. Стандартную сигнатуру функции писать не требуется. Сигнатура создается макросом TEST_METHOD. Макрос создает функцию экземпляра, которая возвращает значение void. Она также создает статическую функцию, которая возвращает сведения о тестовом методе. Эти сведения позволят обозревателю тестов найти этот метод.
    • Тестовые методы группируются в классы с помощью TEST_CLASS(YourClassName) <. >. Во время выполнения тестов создается экземпляр каждого тестового класса. Тестовые методы вызываются в неопределенном порядке. Можно задать особые методы, которые вызываются до и после каждого модуля, класса или метода. Дополнительные сведения см. в разделе Использование Microsoft.VisualStudio.TestTools.CppUnitTestFramework.

Проверка с помощью обозревателя тестов, что тесты запускаются

  1. Добавьте код теста:
TEST_METHOD(TestMethod1)

Test Explorer

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

  • В меню Тест выберите Выполнить, а затем выберите Запустить все. Будет построен и запущен проект теста. Появится окно обозревателя тестов, а тест будет указан в разделе Пройденные тесты. Область сводки в нижней части окна содержит дополнительные сведения о выбранном тесте.
  • Добавление в решение проекта библиотеки DLL

    В обозревателе решений выберите имя решения. В контекстном меню выберите команду Добавить, а затем — пункт Новый проект. В диалоговом окне Добавление нового проекта задайте Язык как C++ и введите «DLL» в поле поиска. В списке результатов выберите Приложение модульного тестирования (универсальная платформа Windows — C++ или CX).

    Create the RooterLib project

    1. В диалоговом окне Добавление нового проекта выберите DLL (UWP apps) (DLL — приложения UWP).
    2. Добавьте следующий код в файл RooterLib.h:

    // The following ifdef block is the standard way of creating macros which make exporting // from a DLL simpler. All files within this DLL are compiled with the ROOTERLIB_EXPORTS // symbol defined on the command line. This symbol should not be defined on any project // that uses this DLL. This way any other project whose source files include this file see // ROOTERLIB_API functions as being imported from a DLL, whereas this DLL sees symbols // defined with this macro as being exported. #ifdef ROOTERLIB_EXPORTS #define ROOTERLIB_API __declspec(dllexport) #else #define ROOTERLIB_API __declspec(dllimport) #endif //ROOTERLIB_EXPORTS class ROOTERLIB_API CRooterLib < public: CRooterLib(void); double SquareRoot(double v); >; 

    Add a preprocessor symbol definition

    Комментарии содержат пояснения к блоку ifdef не только для разработчика библиотеки DLL, но и для тех, кто ссылается на библиотеку DLL в своих проектах. С помощью свойств проекта библиотеки DLL можно добавить в командную строку символ ROOTERLIB_EXPORTS. Класс CRooterLib объявляет конструктор и метод оценки SqareRoot .

  • Добавьте символ ROOTERLIB_EXPORTS в командную строку.
    1. В обозревателе решений выберите проект RooterLib, а затем пункт Свойства в контекстном меню.
    2. В диалоговом окне страницы свойств RooterLib разверните узел Свойства конфигурации, затем — узел C++ и выберите параметр Препроцессор.
    3. Выберите пункт в списке Определения препроцессора, а затем добавьте ROOTERLIB_EXPORTS диалоговом окне Определения препроцессора.
  • Добавьте минимальные реализации объявленных функций. Откройте файл RooterLib.cpp и добавьте следующий код:

    // constructor CRooterLib::CRooterLib() < >// Find the square root of a number. double CRooterLib::SquareRoot(double v)

    Сделать функции dll видимыми для тестового кода

    1. Добавьте RooterLib в проект RooterLibTests.
      1. В обозревателе решений выберите проект RooterLibTests, а затем в контекстном меню последовательно выберите Добавить>Ссылки.
      2. В диалоговом окне Добавление ссылки откройте вкладку Проекты. Затем выберите элемент RouterLib.
      #include "..\RooterLib\RooterLib.h" 
      TEST_METHOD(BasicTest) < CRooterLib rooter; Assert::AreEqual( // Expected value: 0.0, // Actual value: rooter.SquareRoot(0.0), // Tolerance: 0.01, // Message: L"Basic test failed", // Line number - used if there is no PDB file: LINE_INFO()); >

      Basic Test passed

    2. Постройте решение. Новый тест появится в обозревателе тестов в узле Незапускавшиеся тесты.
    3. В обозревателе тестов выберите Запустить все. Вы настроили тест и проекты кода и подтвердили, что можно выполнять тесты, которые запускают функции из проекта кода. Теперь можно начать писать реальные тесты и код.

    Итеративное расширение тестов и обеспечение их успешного выполнения

    1. Добавьте новый тест.
    TEST_METHOD(RangeTest) < CRooterLib rooter; for (double v = 1e-6; v < 1e6; v = v * 3.2) < double expected = v; double actual = rooter.SquareRoot(v*v); double tolerance = expected/1000; Assert::AreEqual(expected, actual, tolerance); >>; 

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

    The RangeTest fails

  • В обозревателе тестов выберите Запустить все.
  • Тест не пройден.

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

    #include . // Find the square root of a number. double CRooterLib::SquareRoot(double v) < double result = v; double diff = v; while (diff >result/1000) < double oldResult = result; result = result - (result*result - v)/(2*result); diff = abs (oldResult - result); >return result; > 

    Разрабатывайте код, добавляя тесты по одному. После каждой итерации проверяйте, все ли тесты завершаются успешно.

    Отладка непройденного теста

    1. Добавьте еще один тест в файл unittest1.cpp:
    // Verify that negative inputs throw an exception. TEST_METHOD(NegativeRangeTest) < wchar_t message[200]; CRooterLib rooter; for (double v = -0.1; v >-3.0; v = v - 0.5) < try < // Should raise an exception: double result = rooter.SquareRoot(v); swprintf_s(message, L"No exception for input %g", v); Assert::Fail(message, LINE_INFO()); >catch (std::out_of_range ex) < continue; // Correct exception. >catch (. ) < swprintf_s(message, L"Incorrect exception for %g", v); Assert::Fail(message, LINE_INFO()); >> >; 

    NegativeRangeTests failed

  • В обозревателе тестов выберите Запустить все. Тест не пройден. Выберите имя теста в обозревателе тестов. Ошибочное проверочное утверждение будет выделено. Сообщение об ошибке отображается в области сведений обозревателя тестов.
  • Чтобы увидеть, почему тест не был пройден, выполните функцию пошагово.
    1. Установите точку останова перед функцией SquareRoot .
    2. В контекстном меню непройденного теста выберите Отладить выбранные тесты. Когда выполнение прекратится на точке останова, выполните код по шагам.
    3. Добавьте код в файл RooterLib.cpp, чтобы перехватить исключение:
    #include . double CRooterLib::SquareRoot(double v) < //Validate the input parameter: if (v < 0.0) < throw std::out_of_range("Can't do square roots of negatives"); >. 

    Теперь все тесты проходят успешно.

    All tests pass

    Рефакторинг кода без изменения тестов

    1. Упростите основной расчет функции SquareRoot :
    // old code //result = result - (result*result - v)/(2*result); // new code result = (result + v/result) / 2.0; 

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

  • Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *