Что такое тестирование

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

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

Вот некоторые примеры поведения в Интернете, которые вы, возможно, захотите протестировать:

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

Автоматизированное и ручное тестирование

Вы можете протестировать свое программное обеспечение двумя основными способами: автоматическое тестирование и ручное тестирование.

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

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

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

Основы на примере

Для нас, веб-разработчиков, пишущих на JavaScript или родственных языках, кратким автоматическим тестом может быть сценарий, подобный этому, который вы запускаете каждый день, возможно, через Node или загружая его в браузер:

import { fibonacci } from "../src/math.js";

if (fibonacci(0) !== 0) {
  throw new Error("Invalid 0th fibonacci result");
}
const fib13 = fibonacci(13);
if (fib13 !== 233) {
  throw new Error("Invalid 13th fibonacci result, was=${fib13} wanted=233");
}

Это упрощенный пример, который дает следующую информацию:

  • Это тест , поскольку он запускает некоторое программное обеспечение ( функцию Фибоначчи ) и гарантирует, что его поведение работает так, как предполагалось, путем проверки его результатов на ожидаемые значения. Если поведение неправильное, это вызывает ошибку, которую JavaScript выражает, выдавая Error .

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

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

Тестирование библиотек на практике

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

Утверждения — это способ совместить проверку результата и возникновение ошибки, если что-то пойдет не так. Например, вы можете сделать предыдущий тест более кратким, введя assert :

import { fibonacci } from "../src/math.js";
import { assert } from "a-made-up-testing-library";

assert.equal(fibonacci(0), 0, "Invalid 0th fibonacci result");
assert.equal(fibonacci(13), 233, "Invalid 13th fibonacci result");

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

import { fibonacci, catalan } from "../src/math.js";
import { assert, test, suite } from "a-made-up-testing-library";

suite("math tests", () => {
  test("fibonacci function", () => {
    assert.equal(fibonacci(0), 0, "Invalid 0th fibonacci result");
    assert.equal(fibonacci(13), 233, "Invalid 13th fibonacci result");
  });
  test("relationship between sequences", () => {
    const numberToCheck = 4;
    const fib = fibonacci(numberToCheck);
    const cat = catalan(numberToCheck);
    assert.isAbove(fib, cat);
  });
});

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

Тесты с индивидуальными именами полезны, среди прочего, для решения следующих задач:

  • Определение успешности или неудачи теста с течением времени.
  • Выделение ошибки или сценария по имени, чтобы вам было легче проверить, решен ли сценарий.
  • Запуск некоторых тестов независимо от других, например, через glob-фильтр.

Один из способов думать о тестовых примерах — использовать «три А» модульного тестирования: организовать, действовать и утверждать. По своей сути каждый тестовый пример будет:

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

Масштаб испытаний

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

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

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

Пирамида тестирования со сквозными (E2E) тестами наверху, интеграционными тестами посередине и модульными тестами внизу.
Пирамида тестирования.

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

Эти типы будут расширены в типах автоматизированного тестирования .

Проверьте свое понимание

Какие примитивы предоставляют большинство библиотек и фреймворков тестирования?

Служба бегуна, использующая облачного провайдера.
Некоторые программы запуска на основе браузера предлагают возможность передать ваши тесты на аутсорсинг, но это не является обычной функцией библиотек тестирования.
Утверждения, которые вызывают исключения, если они не удовлетворены.
Хотя вы можете выдать ошибку, чтобы не пройти тест, assert() и его варианты, как правило, включаются, поскольку они упрощают написание проверок.
Способ категоризации тестов в пирамиду тестирования.
На самом деле не существует стандартного способа сделать это. Вы можете добавлять префиксы к именам своих тестов или размещать их в разных файлах, но категоризация на самом деле не встроена в большинство тестовых платформ.
Возможность определять независимые тесты по функциям.
Метод test() включен почти во все программы запуска тестов. Это важно, поскольку тестовый код не запускается на верхнем уровне файла, что позволяет средству запуска тестов рассматривать каждый тестовый пример как независимую единицу.