Skip to content

Instantly share code, notes, and snippets.

@fabriciofmsilva
Created July 30, 2019 16:41
Show Gist options
  • Save fabriciofmsilva/b3946560246d2b97dd9222320257ee6d to your computer and use it in GitHub Desktop.
Save fabriciofmsilva/b3946560246d2b97dd9222320257ee6d to your computer and use it in GitHub Desktop.
JavaScript Testing
// __tests__/login.js
import React from 'react'
import {render, fireEvent} from '@testing-library/react'
import Login from '../login'
test('calls onSubmit with the username and password when submit is clicked', () => {
  const handleSubmit = jest.fn()
  const {getByLabelText, getByText} = render(<Login onSubmit={handleSubmit} />)
  const user = {username: 'michelle', password: 'smith'}
  fireEvent.change(getByLabelText(/username/i), {
    target: {value: user.username},
  })
  fireEvent.change(getByLabelText(/password/i), {
    target: {value: user.password},
  })
  fireEvent.click(getByText(/submit/i))
  expect(handleSubmit).toHaveBeenCalledTimes(1)
  expect(handleSubmit).toHaveBeenCalledWith(user)
})
test('shows an error message when submit is clicked and no username is provided', () => {
  const handleSubmit = jest.fn()
  const {getByLabelText, getByText, getByRole} = render(
    <Login onSubmit={handleSubmit} />,
  )
  fireEvent.change(getByLabelText(/password/i), {target: {value: 'anything'}})
  fireEvent.click(getByText(/submit/i))
  const errorMessage = getByRole('alert')
  expect(errorMessage).toHaveTextContent(/username is required/i)
  expect(handleSubmit).not.toHaveBeenCalled()
})
test('shows an error message when submit is clicked and no password is provided', () => {
  const handleSubmit = jest.fn()
  const {getByLabelText, getByText, getByRole} = render(
    <Login onSubmit={handleSubmit} />,
  )
  fireEvent.change(getByLabelText(/username/i), {target: {value: 'anything'}})
  fireEvent.click(getByText(/submit/i))
  const errorMessage = getByRole('alert')
  expect(errorMessage).toHaveTextContent(/password is required/i)
  expect(handleSubmit).not.toHaveBeenCalled()
})

Apply AHA (Avoid Hasty Abstractions)

import React from 'react'
import {render, fireEvent} from '@testing-library/react'
import Login from '../login'

// here we have a bunch of setup functions that compose together for our test cases
// I only recommend doing this when you have a lot of tests that do the same thing.
// I'm including it here only as an example. These tests don't necessitate this
// much abstraction. Read more: https://kcd.im/aha-testing
function setup() {
  const handleSubmit = jest.fn()
  const utils = render(<Login onSubmit={handleSubmit} />)
  const user = {username: 'michelle', password: 'smith'}
  const changeUsernameInput = value =>
    fireEvent.change(utils.getByLabelText(/username/i), {target: {value}})
  const changePasswordInput = value =>
    fireEvent.change(utils.getByLabelText(/password/i), {target: {value}})
  const clickSubmit = () => fireEvent.click(utils.getByText(/submit/i))
  return {
    ...utils,
    handleSubmit,
    user,
    changeUsernameInput,
    changePasswordInput,
    clickSubmit,
  }
}

function setupSuccessCase() {
  const utils = setup()
  utils.changeUsernameInput(utils.user.username)
  utils.changePasswordInput(utils.user.password)
  utils.clickSubmit()
  return utils
}

function setupWithNoPassword() {
  const utils = setup()
  utils.changeUsernameInput(utils.user.username)
  utils.clickSubmit()
  const errorMessage = utils.getByRole('alert')
  return {...utils, errorMessage}
}

function setupWithNoUsername() {
  const utils = setup()
  utils.changePasswordInput(utils.user.password)
  utils.clickSubmit()
  const errorMessage = utils.getByRole('alert')
  return {...utils, errorMessage}
}

test('calls onSubmit with the username and password', () => {
  const {handleSubmit, user} = setupSuccessCase()
  expect(handleSubmit).toHaveBeenCalledTimes(1)
  expect(handleSubmit).toHaveBeenCalledWith(user)
})

test('shows an error message when submit is clicked and no username is provided', () => {
  const {handleSubmit, errorMessage} = setupWithNoUsername()
  expect(errorMessage).toHaveTextContent(/username is required/i)
  expect(handleSubmit).not.toHaveBeenCalled()
})

test('shows an error message when password is not provided', () => {
  const {handleSubmit, errorMessage} = setupWithNoPassword()
  expect(errorMessage).toHaveTextContent(/password is required/i)
  expect(handleSubmit).not.toHaveBeenCalled()
})

What about cleanup?

import React from 'react'
import {render, fireEvent, cleanup} from '@testing-library/react'
import Login from '../login'
afterEach(() => cleanup())
test('example 1', () => {
  const handleSubmit = jest.fn()
  const {getByLabelText} = render(<Login onSubmit={handleSubmit} />)
  fireEvent.change(getByLabelText(/username/i), {target: {value: 'mary'}})
  fireEvent.change(getByLabelText(/password/i), {
    target: {value: 'little lamb'},
  })
  // more test here
})
test('example 2', () => {
  const handleSubmit = jest.fn()
  const {getByLabelText} = render(<Login onSubmit={handleSubmit} />)
  fireEvent.change(getByLabelText(/username/i), {target: {value: 'mary'}})
  // more test here
})
let server
beforeAll(async () => {
  server = await startServer()
})
afterAll(() => server.close())
beforeAll(() => {
  jest.spyOn(console, 'error').mockImplementation(() => {})
})
afterEach(() => {
  console.error.mockClear()
})
afterAll(() => {
  console.error.mockRestore()
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment