Модульное тестирование - Unit testing

Разработка программного обеспечения
Активность ядер
Парадигмы и модели
Методологии и рамки
Вспомогательные дисциплины
Практики
Инструменты
Стандарты и свод знаний
Глоссарии
Контуры

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

Описание

Модульные тесты обычно автоматизированный тесты написаны и запущены разработчики программного обеспечения чтобы гарантировать, что раздел приложения (известный как «модуль») соответствует его дизайну и ведет себя так, как задумано.[2] В процедурное программирование, единицей может быть целый модуль, но чаще это отдельная функция или процедура. В объектно-ориентированного программирования, единица часто представляет собой целый интерфейс, например класс, но может быть и отдельным методом.[3] Написав сначала тесты для самых маленьких тестируемых модулей, а затем составив их поведение, можно создать комплексные тесты для сложных приложений.[2]

Чтобы изолировать проблемы, которые могут возникнуть, каждый прецедент следует тестировать самостоятельно. Заменители, такие как заглушки методов, имитировать объекты,[4] подделки, и испытательные жгуты может использоваться для тестирования изолированного модуля.

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

Написание и обслуживание модульных тестов можно ускорить, используя параметризованные тесты. Это позволяет выполнять один тест несколько раз с разными наборами входных данных, что сокращает дублирование кода теста. В отличие от традиционных модульных тестов, которые обычно представляют собой закрытые методы и инвариантные условия тестирования, параметризованные тесты принимают любой набор параметров. Параметризованные тесты поддерживаются TestNG, JUnit и его аналог .Net, XUnit. Подходящие параметры для модульных тестов могут быть предоставлены вручную или в некоторых случаях автоматически генерируются платформой тестирования. В последние годы была добавлена ​​поддержка для написания более мощных (модульных) тестов, использующих концепцию теорий, тестовых примеров, которые выполняют те же шаги, но с использованием тестовых данных, сгенерированных во время выполнения, в отличие от обычных параметризованных тестов, которые используют те же шаги выполнения с входными наборами. которые заранее определены.[5][6][7]

Преимущества

Цель модульного тестирования - изолировать каждую часть программы и показать правильность отдельных частей.[1] Модульный тест обеспечивает строгий письменный договор которому должен удовлетворять фрагмент кода. В результате он дает несколько преимуществ.

Модульное тестирование обнаруживает проблемы на ранней стадии цикл разработки. Сюда входят как ошибки в реализации программиста, так и недостатки или отсутствующие части спецификации модуля. Процесс написания тщательного набора тестов вынуждает автора продумывать входные и выходные данные, а также условия ошибок и, таким образом, более четко определять желаемое поведение устройства. Стоимость обнаружения ошибки до начала кодирования или при первом написании кода значительно ниже, чем стоимость обнаружения, идентификации и исправления ошибки позже. Ошибки в выпущенном коде также могут вызвать дорогостоящие проблемы для конечных пользователей программного обеспечения.[8][9][10] Код может быть невозможным или трудным для модульного тестирования, если он плохо написан, поэтому модульное тестирование может заставить разработчиков лучше структурировать функции и объекты.

В разработка через тестирование (TDD), который часто используется как в экстремальное программирование и схватка, модульные тесты создаются до написания самого кода. Когда тесты проходят, этот код считается завершенным. Для этой функции часто выполняются одни и те же модульные тесты, поскольку большая база кода разрабатывается либо по мере изменения кода, либо с помощью автоматизированного процесса сборки. Если модульные тесты не проходят, это считается ошибкой либо в измененном коде, либо в самих тестах. Затем модульные тесты позволяют легко отследить место неисправности или отказа. Поскольку модульные тесты предупреждают команду разработчиков о проблеме до передачи кода тестировщикам или клиентам, потенциальные проблемы выявляются на ранней стадии процесса разработки.

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

Модульное тестирование может уменьшить неопределенность самих модулей и может использоваться в вверх дном подход стиля тестирования. Сначала проверяя части программы, а затем проверяя их сумму, интеграционное тестирование становится намного легче.[нужна цитата ]

Модульное тестирование обеспечивает своего рода живую документацию системы. Разработчики, которые хотят узнать, какие функции предоставляет модуль и как его использовать, могут взглянуть на модульные тесты, чтобы получить общее представление об интерфейсе модуля (API ).[нужна цитата ]

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

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

Ограничения и недостатки

Тестирование не выявит каждую ошибку в программе, потому что оно не может оценить каждый путь выполнения в любой, кроме самых тривиальных программ. Этот проблема представляет собой надмножество проблема остановки, который неразрешимый. То же верно и для модульного тестирования. Кроме того, модульное тестирование по определению проверяет только функциональность самих модулей. Следовательно, он не будет обнаруживать ошибки интеграции или более широкие ошибки системного уровня (например, функции, выполняемые на нескольких модулях, или нефункциональные области тестирования, такие как спектакль ). Модульное тестирование следует проводить вместе с другими тестирование программного обеспечения действия, так как они могут показать только наличие или отсутствие определенных ошибок; они не могут доказать полное отсутствие ошибок. Чтобы гарантировать правильное поведение для каждого пути выполнения и всех возможных входных данных, а также гарантировать отсутствие ошибок, требуются другие методы, а именно применение формальные методы чтобы доказать, что программный компонент не имеет неожиданного поведения.[нужна цитата ]

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

Тестирование программного обеспечения - это комбинаторная проблема. Например, для каждого логического оператора решения требуется по крайней мере два теста: один с результатом «истина», а другой - с результатом «ложь». В результате для каждой написанной строки кода программистам часто требуется от 3 до 5 строк тестового кода.[11] Это, очевидно, требует времени, и его вложения могут не окупиться. Есть проблемы, которые вообще нелегко проверить - например, те, которые недетерминированный или задействовать несколько потоки. Кроме того, код для модульного теста, вероятно, будет содержать не меньше ошибок, чем код, который он тестирует. Фред Брукс в Мифический человеко-месяц цитаты: «Никогда не выходите в море с двумя хронометрами; возьмите один или три».[12] Имея в виду, если два хронометры противоречить, как узнать, какой из них правильный?

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

Чтобы получить ожидаемые выгоды от модульного тестирования, требуется строгая дисциплина на протяжении всего процесса разработки программного обеспечения. Важно вести тщательный учет не только проведенных тестов, но и всех изменений, которые были внесены в исходный код этого или любого другого модуля программного обеспечения. Использование управление версиями система важна. Если более поздняя версия модуля не проходит определенный тест, который он ранее прошел, программное обеспечение контроля версий может предоставить список изменений исходного кода (если таковые имеются), которые были применены к модулю с того времени.[нужна цитата ]

Также важно внедрить устойчивый процесс для обеспечения того, чтобы сбои тестовых примеров регулярно проверялись и немедленно устранялись.[14] Если такой процесс не реализован и не укоренился в рабочем процессе группы, приложение будет развиваться вне синхронизации с набором модульных тестов, увеличивая количество ложных срабатываний и снижая эффективность набора тестов.

Модульное тестирование встроенного системного программного обеспечения представляет собой уникальную проблему: поскольку программное обеспечение разрабатывается на другой платформе, чем та, на которой оно в конечном итоге будет работать, вы не можете легко запустить тестовую программу в реальной среде развертывания, как это возможно с настольными программами.[15]

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

Пример

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

импорт статических org.junit.Assert. *;импорт org.junit.Test;общественный учебный класс TestAdder {    @Тест    общественный пустота testSumPositiveNumbersOneAndOne() {        Сумматор сумматор = новый AdderImpl();        утверждать(сумматор.Добавить(1, 1) == 2);    }    // можно ли сложить положительные числа 1 и 2?    @Тест    общественный пустота testSumPositiveNumbersOneAndTwo() {        Сумматор сумматор = новый AdderImpl();        утверждать(сумматор.Добавить(1, 2) == 3);    }    // можно ли сложить положительные числа 2 и 2?    @Тест    общественный пустота testSumPositiveNumbersTwoAndTwo() {        Сумматор сумматор = новый AdderImpl();        утверждать(сумматор.Добавить(2, 2) == 4);    }    // нулевая нейтральность?    @Тест    общественный пустота testSumZeroNeutral() {        Сумматор сумматор = новый AdderImpl();        утверждать(сумматор.Добавить(0, 0) == 0);    }    // можно ли складывать отрицательные числа -1 и -2?    @Тест    общественный пустота testSumNegativeNumbers() {        Сумматор сумматор = новый AdderImpl();        утверждать(сумматор.Добавить(-1, -2) == -3);    }    // можно ли добавить положительное и отрицательное?    @Тест    общественный пустота testSumPositiveAndNegative() {        Сумматор сумматор = новый AdderImpl();        утверждать(сумматор.Добавить(-1, 1) == 0);    }    // как насчет больших чисел?    @Тест    общественный пустота testSumLargeNumbers() {        Сумматор сумматор = новый AdderImpl();        утверждать(сумматор.Добавить(1234, 988) == 2222);    }}

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

интерфейс Сумматор {    int Добавить(int а, int б);}учебный класс AdderImpl орудия Сумматор {    общественный int Добавить(int а, int б) {        возвращаться а + б;    }}

Как исполняемые спецификации

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

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

Приложения

Экстремальное программирование

Модульное тестирование - это краеугольный камень экстремальное программирование, который опирается на автоматизированный структура модульного тестирования. Эта автоматизированная среда модульного тестирования может быть сторонней, например, xUnit, или созданный в группе разработчиков.

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

Большая часть кода в системе проходит модульное тестирование, но не обязательно все пути прохождения кода. Экстремальное программирование требует стратегии «проверять все, что может сломаться», а не традиционному методу «проверять каждый путь выполнения». Это заставляет разработчиков разрабатывать меньше тестов, чем классические методы, но на самом деле это не проблема, а скорее повторение фактов, поскольку классические методы редко когда-либо применялись достаточно методично, чтобы все пути выполнения были тщательно протестированы.[нужна цитата ] Экстремальное программирование просто признает, что тестирование редко бывает исчерпывающим (потому что оно часто слишком дорого и требует много времени, чтобы быть экономически жизнеспособным), и дает рекомендации о том, как эффективно сосредоточить ограниченные ресурсы.

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

Модульное тестирование также имеет решающее значение для концепции Эмерджентный дизайн. Поскольку эмерджентный дизайн сильно зависит от рефакторинга, модульные тесты являются неотъемлемым компонентом.[17]

Фреймворки для модульного тестирования

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

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

Поддержка модульного тестирования на уровне языка

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

Языки со встроенной поддержкой модульного тестирования включают:

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

Смотрите также

Рекомендации

  1. ^ а б Колава, Адам; Хейзинга, Дорота (2007). Автоматизированное предотвращение дефектов: передовой опыт управления программным обеспечением. Пресса компьютерного общества Wiley-IEEE. п. 75. ISBN  978-0-470-04212-0.
  2. ^ а б Хэмилл, Пол (2004). Фреймворки модульного тестирования: инструменты для разработки высококачественного программного обеспечения. O'Reilly Media, Inc. ISBN  9780596552817.
  3. ^ Се, Тао. «На пути к структуре дифференциального модульного тестирования объектно-ориентированных программ» (PDF). Получено 23 июля 2012.
  4. ^ Фаулер, Мартин (2 января 2007 г.). "Моки - это не заглушки". Получено 1 апреля 2008.
  5. ^ «Начало работы с xUnit.net (рабочий стол)».
  6. ^ "Теории".
  7. ^ «Параметризованные тесты».
  8. ^ Бем, Барри В.; Папаччо, Филипп Н. (октябрь 1988 г.). «Понимание и контроль затрат на программное обеспечение» (PDF). IEEE Transactions по разработке программного обеспечения. 14 (10): 1462–1477. Дои:10.1109/32.6191. Получено 13 мая 2016.
  9. ^ «Тестируйте рано и часто». Microsoft.
  10. ^ «Докажите, что это работает: использование платформы модульного тестирования для тестирования и проверки программного обеспечения». Национальные инструменты. 21 августа 2017.
  11. ^ Крамблит, Боб (20 сентября 2007 г.). «Альберто Савойя восхваляет тестирование программного обеспечения». Получено 29 ноября 2007.
  12. ^ Брукс, Фредерик Дж. (1995) [1975]. Мифический человеко-месяц. Эддисон-Уэсли. п.64. ISBN  978-0-201-83595-3.
  13. ^ Колава, Адам (1 июля 2009 г.). «Лучшие практики модульного тестирования». Получено 23 июля 2012.
  14. ^ даВейга, Нада (6 февраля 2008 г.). «Меняйте код без страха: используйте страховочную сетку от регресса». Получено 8 февраля 2008.
  15. ^ Кухарский, Марек (23 ноября 2011 г.). «Практическое применение модульного тестирования для разработки встраиваемых систем». Получено 20 июля 2020.
  16. ^ http://wiki.c2.com/?UnitTestsAndDatabases
  17. ^ «Гибкий новый дизайн». Agile Sherpa. 3 августа 2010 г. Архивировано с оригинал 22 марта 2012 г.. Получено 8 мая 2012.
  18. ^ Технология тестирования «яблочко» (2006–2008 гг.). «Промежуточные цели охвата». Получено 24 марта 2009.
  19. ^ «Хрустальный Спец». crystal-lang.org. Получено 18 сентября 2017.
  20. ^ "Модульные тесты - язык программирования D". D Язык программирования. D Language Foundation. Получено 5 августа 2017.
  21. ^ "тестирование - язык программирования Go". golang.org. Получено 3 декабря 2013.
  22. ^ Документация Python (2016 г.). «unittest - фреймворк для модульного тестирования». Получено 18 апреля 2016.
  23. ^ Валлийский, Ноэль; Калпеппер, Райан. «RackUnit: модульное тестирование». PLT Design Inc. Получено 26 февраля 2019.
  24. ^ Валлийский, Ноэль; Калпеппер, Райан. «Пакет RackUnit Unit Testing является частью основного дистрибутива Racket». PLT Design Inc. Получено 26 февраля 2019.
  25. ^ «Минитест (Ruby 2.0)». Ruby-Doc.org.
  26. ^ Разработчики проекта Rust (2011–2014). "Руководство по тестированию на ржавчину (Rust 0.12.0 - до ночи)". Получено 12 августа 2014.
  27. ^ Сьерра, Стюарт. «API для clojure.test - Clojure v1.6 (стабильный)». Получено 11 февраля 2015.
  28. ^ «Pester Framework». Получено 28 января 2016.

внешняя ссылка