2023-04-17

Testing de un formulario en React

En este post, se explica cómo utilizar screen, render y expect para realizar pruebas a un formulario en React.

Render, Screen, Expect, Mocks

Carpetas y archivos a crear

Ejemplo básico

import { render } from '@testing-library/react'

// describe(nombreComponente, logica)

describe('LoginForm', () => {
	// Vamos a tomar "it" como cada parte individual del test.
	it('should render correctly', () => {
		const container = render(<LoginForm />) // renderiza <LoginForm/>
		expect(container).toBeInTheDocument() // Si container esta en el documento pasa el test
	})
})

Ya sabiendo la base de cómo vamos a realizar los tests, podemos comenzar a complejizarlos.

Creación de mocks

// Esta va a ser la información "buena"
export const LoginFormMock = {
	username: 'username',
	password: 'pass123@',
}

// Esta va a ser la información erronea
export const LoginFormMockError = {
	username: 'usernameeeeeeeeeeeeeee',
	password: 'pass123',
}

Dos inputs en pantalla

import { screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { LoginFormMock } from '@/__mocks__/LoginForm.mock.ts'

describe('LoginForm', () => {
	// se ejecuta despues del test y resetea los mocks
	afterEach(cleanup)
	afterEach(jest.clearAllMocks)
	// Este beforeEach es para que se ejecute siempre antes de cualquier test y no tener que repetir constantemente el render
	beforeEach(() => {
		render(<LoginForm />)
	})
	it('should two input and button exists at the screen', () => {
		// Obtenemos los elementos por su rol y name, aunque hay otras formas como el data-testid
		const usernameInput = screen.getByRole('textbox', { name: /Iniciar sesion/i, })
		const passwordInput = screen.getByRole('textbox', { name: /Contraseña/i })
		const buttonSubmit = screen.getByRole('button', { name: /Iniciar sesion/i })

		expect(usernameInput).toBeInTheDocument()
		expect(passwordInput).toBeInTheDocument()
		expect(buttonSubmit).toBeInTheDocument()

		// Aca verifica si el botón está deshabilitado cuando el form no tiene información.
		expect(buttonSubmit).toBeDisabled()
	})

Mismos dos inputs y le sumamos mocks, userEvent

it('should enable the submit button if the form values are valid', async () => {
	const usernameInput = screen.getByRole('textbox', { name: /Iniciar sesion/i })
	const passwordInput = screen.getByRole('textbox', { name: /Contraseña/i })
	const submitButton = screen.getByRole('button', { name: /Iniciar sesion/i })

	expect(submitButton).tobeDisabled()

	// Ahora simulamos que el usuario escribe en el form con la información del mock
	await userEvent.type(usernameInput, LoginFormMock.username)
	await userEvent.type(usernameInput, LoginFormMock.password)

	// El waitFor es una función que espera a que todo lo que está dentro termine; aquí no se utiliza de la mejor manera
	// pero es para el ejemplo
	await waitFor(() => {
		// Vemos si el valor del form conincide con el mock
		expect(usernameInput).toHaveValue(LoginFormMock.username)
		expect(passwordInput).toHaveValue(LoginFormMock.password)
	})
	// y vemos que se habilite el boton
	expect(submitButton).toBeEnabled()
})