Перевод статьи подготовлен в преддверии старта курса «Автоматизация тестирования на JavaScript».
Моки — не кусаются!
Они предназначены помочь вам создавать более простые и надежные тесты. В этой серии статей я продемонстрирую вам паттерны, на которые я опираюсь при написании моков (mocks или “заглушек”) компонентов React.
Вот наглядный пример заглушки компонента. Я использую
Типичная заглушка React компонента не должна выглядеть сложнее. Следует обратить внимание на очень простое значение-заглушку (
Прежде чем мы рассмотрим, как они используются, давайте сначала вспомним, что такое моки и почему вы вообще можете захотеть их использовать.
В мире JavaScript термин mock очень широко применяется для обозначения любой имитированной реализации или тестового двойника (test double). Имитированные реализации — это просто значения, которые заменяют другие в вашем производственном коде во время выполнения тестов. Они примеряют на себя интерфейс заменяемого объекта, так что остальная часть вашего кода работает так, как если бы никакой замены не было.
Есть несколько разных причин, по которым вам может это понадобиться; мы рассмотрим их на примерах.
Если вам интересно узнать больше о имитированных реализациях, почитайте книгу Мартина Фаулера «Моки — не заглушки».
В Jest есть функция
В книге Mastering React Test-Driven Development я использую импорт именованных модулей ES6 для создания тестовых двойников. Такой подход дает немного больше гибкости, но выглядит слегка более хакерским.
Хотя это правда, это не основная причина, по которой я использую моки.
Я использую моки, потому что они помогают мне поддерживать мои тесты независимыми друг от друга.
Чтобы понять, почему это так, давайте рассмотрим пример.
Ниже приведен листинг компонента
Представьте, что вы пишете тесты для этого компонента, и все ваши тесты включены в
На этом этапе моки вам еще не нужны: мы еще не видели
Теперь представьте, что и у
Поддерживать тесты в рабочем состоянии становится сложнее. Каждый новый тест имеет более сложный сетап, и набор тестов начинает поедать все больше вашего времени — бремя, которое теперь необходимо поддерживать.
Это распространенная проблема, и я все время вижу ее в кодовых базах React. Наборы тестов, в которых даже простейшее изменение результирует в сбоях многих тестов.
Одно из решений — разделить тестовые наборы. Мы оставим
Хорошо.
Но что, если рендеринг
Набор тестов в
Зависимость, которой мы пытались избежать путем разделения наших тестовых наборов, все еще существует.
Организация нашего тестирования, безусловно, стала лучше, но в конечном итоге ничего, что бы сделало наши тесты менее хрупки, не произошло.
Для этого нам нужна заглушка (или мок)
И в следующей части мы рассмотрим, как это сделать.
Кстати, пара слов о переходе из области сквозных тестов в область модульных тестов.
Наличие тестовых двойников — ключевой показатель того, что вы пишете модульные тесты.
Многие опытные тестировщики сразу же начинают новые проекты с модульных тестов (и моков), потому что они знают, что по мере роста их кодовой базы они будут сталкиваться с проблемой нестабильности тестов.
Даже когда мы проводим модульное тестирование, тестовые двойники не всегда необходимы — это просто еще один инструмент в вашем наборе, который вы должны знать, когда и как применять.
В следующей части мы рассмотрим основные техники мокинга.
Моки — не кусаются!
Они предназначены помочь вам создавать более простые и надежные тесты. В этой серии статей я продемонстрирую вам паттерны, на которые я опираюсь при написании моков (mocks или “заглушек”) компонентов React.
Вот наглядный пример заглушки компонента. Я использую
jest.mock
, который мы рассмотрим подробнее ниже.jest.mock("../src/PostContent", () => ({
PostContent: jest.fn(() => (
<div data-testid="PostContent" />
))
}))
Типичная заглушка React компонента не должна выглядеть сложнее. Следует обратить внимание на очень простое значение-заглушку (
div
) и атрибут data-testid
, который позволяет нам очень легко найти в DOM отрисованный экземпляр. По соглашению, используемый тестовый идентификатор должен совпадать с именем компонента. В данном случае это PostContent
.Прежде чем мы рассмотрим, как они используются, давайте сначала вспомним, что такое моки и почему вы вообще можете захотеть их использовать.
Что такое мок?
В мире JavaScript термин mock очень широко применяется для обозначения любой имитированной реализации или тестового двойника (test double). Имитированные реализации — это просто значения, которые заменяют другие в вашем производственном коде во время выполнения тестов. Они примеряют на себя интерфейс заменяемого объекта, так что остальная часть вашего кода работает так, как если бы никакой замены не было.
Есть несколько разных причин, по которым вам может это понадобиться; мы рассмотрим их на примерах.
Если вам интересно узнать больше о имитированных реализациях, почитайте книгу Мартина Фаулера «Моки — не заглушки».
Jest и мокинг
В Jest есть функция
jest.mock
, которая позволяет мокать целые модули, которые вы заменяете. В этом руководстве я сосредоточился именно на этой функции, хотя в JavaScript есть и другие способы замены объектов.В книге Mastering React Test-Driven Development я использую импорт именованных модулей ES6 для создания тестовых двойников. Такой подход дает немного больше гибкости, но выглядит слегка более хакерским.
На странице Jest про jest.mock
говорится, что моки гарантируют, что ваши тесты будут быстрыми и не хрупкими.Хотя это правда, это не основная причина, по которой я использую моки.
Я использую моки, потому что они помогают мне поддерживать мои тесты независимыми друг от друга.
Чтобы понять, почему это так, давайте рассмотрим пример.
Почему моки?
Ниже приведен листинг компонента
BlogPage
, который выполняет две функции: он извлекает id
из свойства url
и затем отображает PostContent
компонент с этим id
.const getPostIdFromUrl = url =>
url.substr(url.lastIndexOf("/") + 1)
export const BlogPage = ({ url }) => {
const id = getPostIdFromUrl(url)
return (
<PostContent id={id} />
)
}
Представьте, что вы пишете тесты для этого компонента, и все ваши тесты включены в
BlogPage.test.js
, который представляет собой единый набор тестов, покрывающий компоненты BlogPage
и PostContent
.На этом этапе моки вам еще не нужны: мы еще не видели
PostContent
, но, учитывая размер BlogPage
, действительно нет необходимости иметь два отдельных набора тестов, поскольку BlogPage
— это в целом просто PostContent
.Теперь представьте, что и у
BlogPage
, и у PostContent
прибавляется функциональность. Вы, как одаренный разработчик, каждый день добавляете все больше и больше функций.Поддерживать тесты в рабочем состоянии становится сложнее. Каждый новый тест имеет более сложный сетап, и набор тестов начинает поедать все больше вашего времени — бремя, которое теперь необходимо поддерживать.
Это распространенная проблема, и я все время вижу ее в кодовых базах React. Наборы тестов, в которых даже простейшее изменение результирует в сбоях многих тестов.
Одно из решений — разделить тестовые наборы. Мы оставим
BlogPage.test.js
и создадим новый PostContent.test.js
, который должен содержать тесты исключительно для PostContent
. Основная идея заключается в том, что любые функции, размещенные в PostContent
, должны быть указаны в PostContent.test.js
, а любые функции, размещенные в BlogPage
(например, парсинг URL), должны быть в BlogPage.test.js
.Хорошо.
Но что, если рендеринг
PostContent
имеет побочные эффекты?export const PostContent = ({ id }) => {
const [ text, setText ] = useState("")
useEffect(() => {
fetchPostContent(id)
}, [id])
const fetchPostContent = async () => {
const result = await fetch(`/post?id=${id}`)
if (result.ok) {
setText(await result.text())
}
}
return <p>{text}</p>
};
Набор тестов в
BlogPage.test.js
должен знать о побочных эффектах и быть готовым обрабатывать их. Например, он должен будет держать наготове fetch
ответ.Зависимость, которой мы пытались избежать путем разделения наших тестовых наборов, все еще существует.
Организация нашего тестирования, безусловно, стала лучше, но в конечном итоге ничего, что бы сделало наши тесты менее хрупки, не произошло.
Для этого нам нужна заглушка (или мок)
PostContent
.И в следующей части мы рассмотрим, как это сделать.
Так ли это необходимо?
Кстати, пара слов о переходе из области сквозных тестов в область модульных тестов.
Наличие тестовых двойников — ключевой показатель того, что вы пишете модульные тесты.
Многие опытные тестировщики сразу же начинают новые проекты с модульных тестов (и моков), потому что они знают, что по мере роста их кодовой базы они будут сталкиваться с проблемой нестабильности тестов.
Модульные тесты обычно намного меньше, чем сквозные. Они могут быть настолько малы, что часто занимают не больше трех-четырех строк кода. Это делает их отличными кандидатами для таких практик социального кодирования, как парное и ансамблевое программирование.
Даже когда мы проводим модульное тестирование, тестовые двойники не всегда необходимы — это просто еще один инструмент в вашем наборе, который вы должны знать, когда и как применять.
В следующей части мы рассмотрим основные техники мокинга.