stringtranslate.com

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

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

История

До модульного тестирования инструменты захвата и воспроизведения были нормой. В 1997 году Кент Бек и Эрих Гамма разработали и выпустили JUnit , среду модульного тестирования, которая стала популярной среди разработчиков Java . [2] Google внедрил автоматическое тестирование примерно в 2005–2006 годах. [3]

Единица

Unit — это наименьший компонент, который можно изолировать в сложной структуре приложения. [4] Это может быть функция, подпрограмма, метод или свойство.

Модульные тесты — это автоматические тесты или ручные тесты, написанные и выполняемые разработчиками программного обеспечения , чтобы гарантировать, что раздел приложения (известный как «модуль») соответствует своему дизайну и ведет себя так, как задумано. [5] Они часто выполняются разработчиком, который изначально написал код, в качестве первой линии защиты перед проведением дальнейшего тестирования.

Процедурное программирование

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

Объектно-ориентированного программирования

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

Критерии тестирования

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

Прецедент

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

Параметризованный тест

Параметризованные тесты — это метод, который утверждает, что сокращает процесс написания и поддержки модульных тестов. Параметризованные тесты позволяют выполнять один тест несколько раз с разными входными наборами, тем самым уменьшая дублирование тестового кода. В отличие от традиционных модульных тестов, которые обычно представляют собой закрытые методы и проверяют инвариантные условия, параметризованные тесты принимают любой набор параметров. Параметризованные тесты поддерживаются TestNG , JUnit [8] и его аналогами .Net, XUnit и NUnit , а также в различных средах тестирования JavaScript. [ нужна цитата ]

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

Гибкий

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

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

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

Раннее обнаружение проблем в цикле разработки

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

Сниженная стоимость

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

Разработка через тестирование

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

Более частые выпуски

Модульное тестирование позволяет чаще выпускать выпуски при разработке программного обеспечения. Тестируя отдельные компоненты по отдельности, разработчики могут быстро выявлять и устранять проблемы, что приводит к ускорению циклов итерации и выпуска. [12]

Позволяет проводить рефакторинг кода

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

Обнаруживает изменения, которые могут нарушить контракт на проектирование

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

Уменьшите неопределенность

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

Документирование поведения системы

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

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

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

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

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

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

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

Сложность в настройке реалистичных и полезных тестов.

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

Требует дисциплины на протяжении всего процесса разработки.

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

Требуется контроль версий

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

Требует регулярных проверок

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

Ограничения для встроенного системного программного обеспечения

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

Ограничения для тестирования интеграции с внешними системами

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

Примеры

Джава

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

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

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

интерфейс  Adder { int add ( int a , int b ); } Класс AdderImpl реализует Adder { public int add ( int a , int b ) { return a + b ; } }                      

В качестве исполняемых спецификаций

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

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

Приложения

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. ^ аб Колава, Адам; Хейзинга, Дорота (2007). Автоматизированное предотвращение дефектов: лучшие практики управления программным обеспечением . Издательство компьютерного общества Wiley-IEEE. п. 75. ИСБН 978-0-470-04212-0.
  2. ^ Гулати, Шекхар (2017). Модульное тестирование Java с помощью JUnit 5: разработка через тестирование с помощью JUnit 5 . Рахул Шарма. Беркли, Калифорния: Apress. п. 8. ISBN 978-1-4842-3015-2. ОКЛК  1012347252.
  3. ^ Уинтерс, Титус (2020). Разработка программного обеспечения в Google: уроки, извлеченные из программирования с течением времени . Том Маншрек, Хайрам Райт (1-е изд.). Севастополь, Калифорния: О'Рейли. ISBN 978-1-4920-8274-3. OCLC  1144086840.
  4. ^ «Общие сведения о Visual Studio 2003» . VS2003_General_en-us.pdf: Корпорация Microsoft. 2016. с. 4405 . Проверено 6 февраля 2024 г.{{cite web}}: CS1 maint: location (link)
  5. ^ Аб Хэмилл, Пол (2004). Платформы модульного тестирования: инструменты для разработки высококачественного программного обеспечения. O'Reilly Media, Inc. ISBN 9780596552817.
  6. ^ Се, Тао. «К структуре дифференциального модульного тестирования объектно-ориентированных программ» (PDF) . Архивировано из оригинала (PDF) 23 июля 2012 года . Проверено 23 июля 2012 г.
  7. Фаулер, Мартин (2 января 2007 г.). «Моки — это не заглушки» . Проверено 1 апреля 2008 г.
  8. ^ Гулати и Шарма 2017, стр. 133–137, глава §7 Модель расширения JUnit 5 — параметризованный тест.
  9. ^ Бем, Барри В .; Папаччо, Филип Н. (октябрь 1988 г.). «Понимание и контроль затрат на программное обеспечение» (PDF) . Транзакции IEEE по разработке программного обеспечения . 14 (10): 1462–1477. дои : 10.1109/32.6191. Архивировано из оригинала (PDF) 9 октября 2016 года . Проверено 13 мая 2016 г.
  10. ^ «Тестируйте рано и часто» . Майкрософт.
  11. ^ «Докажите, что это работает: использование платформы модульного тестирования для тестирования и проверки программного обеспечения» . Национальные инструменты . 21 августа 2017 г.
  12. Эрик (10 марта 2023 г.). «Вы все еще не знаете, как проводить модульное тестирование (и ваш секрет в безопасности со мной)». Стекировать . Проверено 10 марта 2023 г.
  13. ^ Брукс, Фредерик Дж. (1995) [1975]. Мифический человеко-месяц . Аддисон-Уэсли. п. 64. ИСБН 978-0-201-83595-3.
  14. daVeiga, Нада (6 февраля 2008 г.). «Изменяйте код без страха: используйте систему регрессионной безопасности» . Проверено 8 февраля 2008 г.
  15. Кучарски, Марек (23 ноября 2011 г.). «Практическое использование модульного тестирования для разработки встраиваемых систем» . Проверено 20 июля 2020 г.
  16. ^ «Юнит-тесты и базы данных» . Проверено 29 января 2024 г.
  17. ^ Технология тестирования Bullseye (2006–2008). «Промежуточные цели охвата» . Проверено 24 марта 2009 г.
  18. ^ «Юнит-тесты — язык программирования D» . D Язык программирования . Языковой фонд D. Проверено 5 августа 2017 г.
  19. ^ Стив Клабник и Кэрол Николс при участии сообщества Rust (2015–2023 гг.). «Как писать тесты» . Проверено 21 августа 2023 г.
  20. ^ "Кристалл Спец". Crystal-lang.org . Проверено 18 сентября 2017 г.
  21. ^ «тестирование — язык программирования Go» . golang.org . Проверено 3 декабря 2013 г.
  22. ^ «Модульное тестирование · Язык Джулии» . docs.julialang.org . Проверено 15 июня 2022 г.
  23. ^ Документация Python (2016). «unittest — среда модульного тестирования» . Проверено 18 апреля 2016 г.
  24. ^ Валлийский, Ноэль; Калпеппер, Райан. «RackUnit: Модульное тестирование». ПЛТ Дизайн Инк . Проверено 26 февраля 2019 г.
  25. ^ Валлийский, Ноэль; Калпеппер, Райан. «Пакет RackUnit Unit Testing, входящий в основной дистрибутив Racket». ПЛТ Дизайн Инк . Проверено 26 февраля 2019 г.
  26. ^ «Минитест (Ruby 2.0)» . Руби-Док.org.
  27. ^ Сьерра, Стюарт. «API для clojure.test — Clojure v1.6 (стабильный)» . Проверено 11 февраля 2015 г.
  28. ^ "Пестер Фреймворк" . Гитхаб . Проверено 28 января 2016 г.

дальнейшее чтение

Внешние ссылки