Тестирование react-hook-form с помощью react-native-testing-library

Я использую react-hook-form как для веб, так и для react native без единой проблемы. Отличная библиотека. При использовании react-hook-form v6. я столкнулся с проблемой, когда валидация отлично работает в коде, но в тесте объект ошибок всегда пуст даже для неправильного значения. Давайте посмотрим, как решить эту проблему. В этом блоге я покажу, как протестировать react-hook-form с помощью react-native-testing-library для iOS и Android в рамках одного теста с помощью jest-expo, и да, мы будем использовать jest в качестве тестового бегуна.

1. Настройка проекта

Я буду использовать expo для быстрой демонстрации.

# select blank template, JS or TS
expo init test-rhf

cd test-rhf
yarn add react-hook-form
yarn add --dev react-native-testing-library jest-expo

Войти в полноэкранный режим Выход из полноэкранного режима

jest-expo — это библиотека от команды Expo для универсального тестирования, она будет запускать ваши тесты для каждой платформы, которую вы установите, здесь, поскольку react-native-testing-library поддерживает только нативные устройства, мы не будем использовать веб-установку, через expo’s file extension pick up и сходство между react-native-testing-library и @testing-library/react, поделиться одним единственным тестовым файлом для веб- и нативных устройств должно быть легко, попробуем позже.

В package.json:

  • добавьте скрипт: «test»: «node_modules/.bin/jest».

  • добавьте настройки jest:

"jest": {
    "projects": [
      {
        "preset": "jest-expo/ios",
        "setupFilesAfterEnv": [
          "<rootDir>/jestAfterEnvSetup.js"
        ]
      },
      {
        "preset": "jest-expo/android",
        "setupFilesAfterEnv": [
          "<rootDir>/jestAfterEnvSetup.js"
        ]
      }
    ]
  }
Войти в полноэкранный режим Выйти из полноэкранного режима

Если вы хотите переписать какие-либо правила jest, вы должны написать новое правило для каждой платформы, как в примере выше.

создайте jestAfterEnvSetup.js:

global.window = {};
global.window = global;

Вход в полноэкранный режим Выход из полноэкранного режима

Если вы используете Typescript, вам может понадобиться // @ts-ignore вышеприведенные строки

2. Приложение для тестирования

Измените файл App.js на следующий:

import React from "react";
import { Text, Button, TextInput, View } from "react-native";
import { useForm, Controller } from "react-hook-form";

export default function App() {
  const { errors, control, handleSubmit } = useForm({
    defaultValues: { name: "" },
  });

  const errorText = errors["name"]?.message;
  const isError = Boolean(errorText);

  return (
    <View style={{ margin: 10 }}>
      <Controller
        control={control}
        render={({ onChange, onBlur, value }) => (
          <TextInput
            style={{ borderColor: "black" }}
            testID="nameInput"
            onChangeText={onChange}
            onBlur={onBlur}
            value={value}
          />
        )}
        rules={{ required: "name can't be blank" }}
        name="name"
      />

      {isError && <Text testID="nameErrorText">{errorText}</Text>}

      <Button
        testID="submitButton"
        title="submit"
        onPress={handleSubmit(async ({ name }) => {
          console.log(name);
        })}
      />
    </View>
  );
}
Войти в полноэкранный режим Выйти из полноэкранного режима

У нас есть простая форма, один текстовый ввод для имени, и он обязателен, одна кнопка отправки для отправки значений.

Запустив expo, вы увидите это уродливое приложение, нажатие кнопки submit с пустым вводом приведет к ошибке.

Мы извлекаем errorText из errors[«name»]?.message, затем используем Boolean(errorText) для проверки, если есть текст ошибки, значит ошибка есть.

3. Тест

Создайте файл App.test.js на том же уровне App.js со следующим содержимым:

import * as React from "react";
import App from "./App";
import { render, fireEvent, act } from "react-native-testing-library";

it("should not trigger error for correct values", async () => {
  const { getByTestId, queryByTestId } = render(<App />);

  fireEvent.changeText(getByTestId("nameInput"), "ABCDEFG");

  await act(async () => {
    fireEvent.press(getByTestId("submitButton"));
  });

  expect(queryByTestId("nameErrorText")).not.toBeTruthy();
});

it("should trigger error for empty input", async () => {
  const { getByTestId, queryByTestId } = render(<App />);

  await act(async () => {
    fireEvent.press(getByTestId("submitButton"));
  });

  expect(queryByTestId("nameErrorText")).toBeTruthy();
});
Вход в полноэкранный режим Выйти из полноэкранного режима

Здесь у нас есть 2 теста, один для счастливого пути, а другой для несчастливого.
Мы будем имитировать поведение пользователя.

Для счастливого пути: Если у нас есть значение, то текст nameErrorText не должен отображаться.
Для несчастливого пути: Если у нас нет значения, то приложение должно показать текст nameErrorText.

Тесты должны быть довольно простыми для чтения. Я не буду объяснять их здесь.

Самая интересная часть:

await act(async () => {
  fireEvent.press(getByTestId("submitButton"));
});
Вход в полноэкранный режим Выход из полноэкранного режима

Почему нам нужно ожидать act(async()=>{}) события нажатия кнопки? Потому что валидация в react-hook-form всегда асинхронна, поэтому вам придется ждать, пока она завершится. (Что хорошо, потому что в реальном мире валидация может быть дорогостоящей).

Если вы забудете обернуть act(), вы увидите красное предупреждение: Warning: Обновление App внутри теста не было обернуто в act(…).

4. Завершите .

Запустите тест yarn, и вы увидите, что все тесты пройдены.

Спасибо за прочтение! Надеюсь, это поможет.

Следите за мной (albertgao) в twitter, если хотите узнать больше о моих интересных идеях.

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *