Эта статья заслуживает вашего внимания, если вы Вы увлечены написанием качественного программного обеспечения и хотите повысить стабильность своего приложения с помощью тестов. Устали от неожиданных ошибок, возникающих в ваших производственных системах. Нужна помощь в понимании того, что такое автоматизированные тесты и как к ним подходить. Зачем нам нужны автоматизированные тесты? Как инженеры, мы хотим , но с каждой новой функцией, которую мы создаем, мы неизбежно увеличиваем размер и сложность наших приложений. создавать вещи , которые работают По мере роста продукта становится все больше и больше времени уходит на (например, руками) тестирование каждой функциональности, на которую влияют ваши изменения. ручное Отсутствие автоматизированных тестов приводит к тому, что мы либо тратим слишком много времени и замедляем скорость доставки, либо тратим слишком мало, чтобы сохранить скорость, что приводит к появлению новых ошибок в бэклоге и ночным звонкам от PagerDuty. Напротив, . Итак, компьютеры можно запрограммировать на многократное выполнение одного и того же давайте делегируем тестирование компьютерам! Виды тестов предполагает . Давайте углубимся в каждый вид и поймем, почему каждый из них нам нужен. Идея пирамиды тестирования три основных типа тестов: модульные, интеграционные и сквозные Модульные тесты — это небольшой фрагмент логики, который вы тестируете (не полагаясь на другие компоненты). Модуль изолированно Они заканчиваются в течение нескольких секунд. позволяет им запускать их в любой момент времени, локально и в CI, без запуска зависимых сервисов/выполнения вызовов API и базы данных. Модульные тесты выполняются быстро. Изоляция функция, которая принимает два числа и суммирует их. Мы хотим вызвать его с разными аргументами и подтвердить, что возвращаемое значение правильное. Пример модульного теста: // Function "sum" is the unit const sum = (x, y) => x + y test('sums numbers', () => { // Call the function, record the result const result = sum(1, 2); // Assert the result expect(result).toBe(3) }) test('sums numbers', () => { // Call the function, record the result const result = sum(5, 10); // Assert the result expect(result).toBe(15) }) Более интересный пример — компонент React, который отображает некоторый текст после завершения запроса API. Нам нужно смоделировать модуль API, чтобы он возвращал необходимые значения для наших тестов, визуализировал компонент и утверждал, что визуализированный HTML содержит необходимое нам содержимое. // "MyComponent" is the unit const MyComponent = () => { const { isLoading } = apiModule.useSomeApiCall(); return isLoading ? <div>Loading...</div> : <div>Hello world</div> } test('renders loading spinner when loading', () => { // Mocking the API module, so that it returns the value we need jest.mock(apiModule).mockReturnValue(() => ({ useSomeApiCall: jest.fn(() => ({ // Return "isLoading: false" for this test case isLoading: false })) })) // Execute the unit (render the component) const result = render(<MyComponent />) // Assert the result result.findByText('Loading...').toBeInTheDocument() }) test('renders text content when not loading', () => { // Mocking the API module jest.mock(apiModule).mockReturnValue(() => ({ useSomeApiCall: jest.fn(() => ({ // Return "isLoading: false" for this test case isLoading: false })) })) // Execute the unit (render the component) const result = render(<MyComponent />) // Assert the result result.findByText('Hello world').toBeInTheDocument() }) Интеграционные тесты Когда ваш взаимодействует с другими , мы называем это . Эти тесты медленнее, чем модульные тесты, но они проверяют, как соединяются части вашего приложения. модуль модулями (зависимостями) интеграцией служба, которая создает пользователей в базе данных. Для этого требуется, чтобы экземпляр БД ( ) был доступен при выполнении тестов. Мы проверим, что сервис может создавать и извлекать пользователя из БД. Пример интеграционного теста: зависимость import db from 'db' // We will be testing "createUser" and "getUser" const createUser = name => db.createUser(name) // creates a user const getUser = name => db.getUserOrNull(name) // retrieves a user or null test("creates and retrieves users", () => { // Try to get a user that doesn't exist, assert Null is returned const nonExistingUser = getUser("i don't exist") expect(nonExistingUser).toBe(null); // Create a user const userName = "test-user" createUser(userName); // Get the user that was just created, assert it's not Null const user = getUser(userName); expect(user).to.not.be(null) }) Сквозные тесты Это тест, когда мы тестируем , в котором доступны все его зависимости. Эти тесты лучше всего имитируют реальное поведение пользователя и позволяют выявить в вашем приложении, но это самый тип тестов. сквозной полностью развернутое приложение все возможные проблемы медленный Всякий раз, когда вы хотите запустить сквозное тестирование, вы должны подготовить всю инфраструктуру и убедиться, что сторонние поставщики доступны в вашей среде. Вы хотите использовать их для функций вашего приложения. только критически важных Процесс входа в систему. Мы хотим зайти в приложение, заполнить данные для входа, отправить их и увидеть приветственное сообщение. Давайте рассмотрим пример сквозного теста: test('user can log in', () => { // Visit the login page page.goto('https://example.com/login'); // Fill in the login form page.fill('#username', 'john'); page.fill('#password', 'some-password'); // Click the login button page.click('#login-button'); // Assert the welcome message is visible page.assertTextVisible('Welcome, John!') }) Как вы выбираете, какой тип теста написать? Помните, что , а . сквозные тесты медленнее интеграционных интеграционные тесты медленнее модульных Если функция, над которой вы работаете, является критически важной, рассмотрите возможность написания хотя бы одного теста (например, проверки того, как работает функция входа в систему при разработке потока аутентификации). сквозного Помимо критически важных потоков, мы хотим протестировать как можно больше крайних случаев и различных состояний функции. позволяют нам проверить, как части приложения работают вместе. Интеграционные тесты Конечные точки должны выполнять операции, давать ожидаемый результат и не выдавать неожиданных ошибок. Хорошей идеей является проведение интеграционных тестов для конечных точек и клиентских компонентов. Клиентские компоненты должны отображать правильный контент и реагировать на взаимодействие с пользователем так, как вы ожидаете от них. И, наконец, когда нам следует выбирать ? Все небольшие функции, которые можно тестировать изолированно, например , суммирующий числа, , отображающий тег , являются отличными кандидатами для модульных тестов. Модули идеальны, если вы следуете подходу . модульные тесты sum Button <button> разработки через тестирование Что дальше? (но начните с малого) Напишите несколько тестов! , подходящую вашему проекту/языку. На каждом языке есть популярная библиотека для тестирования, например / для JavaScript, / для сквозного тестирования (также использует JavaScript), для Java и т. д. Установите среду тестирования Jest Vitest Cypress Playwright JUnit Найдите в своем проекте небольшую функцию и напишите для нее тест. модульный Напишите тест для взаимодействия некоторого компонента/сервиса с базой данных. интеграционный Выберите критический сценарий, который можно быстро протестировать, например простой процесс входа в систему, и напишите для него тест. сквозной Сделайте все вышеперечисленное один раз, чтобы понять, как это работает. Затем сделайте это снова во время работы над какой-либо функцией или ошибкой. Тогда поделитесь им со своими коллегами, чтобы вы все писали тесты, экономили время и лучше спали по ночам! Полезные ресурсы: Мой личный блог Пирамида практических испытаний Хэма Воке Разработка через тестирование Мартина Фаулера