---
title: Bitovi Academy - Testing in React
description: Write unit tests to verify components are functioning as expected.
---

- ![AI implementation](https://www.bitovi.com/hubfs/AIConsultingIcon.svg)
  
  [AI implementation](https://www.bitovi.com/services/ai-consulting?hsLang=en-us)
- ![Systems engineering](https://www.bitovi.com/hubfs/icon%20-%20backend.svg)
  
  [Systems engineering](https://www.bitovi.com/services/systems-engineering-consulting?hsLang=en-us)
- ![Project management](https://www.bitovi.com/hubfs/icon%20-%20PM.svg)
  
  [Project management](https://www.bitovi.com/services/agile-project-management-consulting?hsLang=en-us)
- ![Product design](https://www.bitovi.com/hubfs/icon%20-%20design.svg)
  
  [Product design](https://www.bitovi.com/services/product-design-consulting?hsLang=en-us)
- ![Frontend development](https://www.bitovi.com/hubfs/icon%20-%20frontend.svg)
  
  [Frontend development](https://www.bitovi.com/services/frontend-development-consulting?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/digital-consulting-services?hsLang=en-us)

We're Experts in...

- [JavaScript](https://www.bitovi.com/services/frontend/javascript-consulting?hsLang=en-us)
- [AI training](https://www.bitovi.com/ai-training-for-software-engineers?hsLang=en-us)
- [Angular](https://www.bitovi.com/services/frontend/angular-consulting?hsLang=en-us)
- [Design Systems](https://www.bitovi.com/services/axure-figma-migration?hsLang=en-us)
- [React](https://www.bitovi.com/services/frontend/react-consulting?hsLang=en-us)
- [Temporal](https://www.bitovi.com/services/backend/temporal-consulting?hsLang=en-us)
- [React Native](https://www.bitovi.com/services/frontend/react-consulting/react-native?hsLang=en-us)
- [Node.js](https://www.bitovi.com/services/backend/nodejs-consulting-1?hsLang=en-us)

Showcase

![Yum! Brands](https://www.bitovi.com/hubfs/yum-showcase-link-1.png)

[View case study](https://www.bitovi.com/en/bitovi-yum-case-study?hsLang=en-us)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign?hsLang=en-us)
- [![Christie's International Real Estate](https://www.bitovi.com/hubfs/christies.svg)](https://design.bitovi.com/christies)
- [![BAFS](https://www.bitovi.com/hubfs/bafs.svg)](https://www.bitovi.com/ux-design-consulting/ux-case-studies/bafs-ppp?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work?hsLang=en-us)

Open Source Tools

We build powerful tools and open source them to support the community.

[See what we've built →](https://www.bitovi.com/open-source?hsLang=en-us)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about perfecting software delivery
  
  ](https://bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships?hsLang=en-us)
- [![Academy](https://www.bitovi.com/hubfs/limbo/icons/icon-academy.svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy/?hsLang=en-us)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse our tools or contribute to our open source mission.
  
  ](https://www.bitovi.com/open-source?hsLang=en-us)

Let's Connect

- [![Discord](https://www.bitovi.com/hubfs/DiscordLogo.svg)
  
  Discord
  
  ](https://discord.gg/J7ejFsZnJ4)
- [![LinkedIn](https://www.bitovi.com/hubfs/LinkedinLogo.svg)
  
  LinkedIn
  
  ](https://www.linkedin.com/company/bitovi/)
- [![GitHub](https://www.bitovi.com/hubfs/GithubLogo.svg)
  
  GitHub
  
  ](https://github.com/bitovi/)

![Eggbot](https://www.bitovi.com/hubfs/build_assets/bitovi-limbo-cms-react/338/js_client_assets/assets/eggbot-LTGhdSGL.png)

Name *

Work Email *

Phone

What's your project?

Send

### Contact Us

(312) 620-0386contact@bitovi.com

- [ Advanced JavaScript ](https://www.bitovi.com/academy/learn-advanced-javascript.html?hsLang=en-us)
- [ DOM and jQuery ](https://www.bitovi.com/academy/learn-dom-jquery.html?hsLang=en-us)
- [ Debug JavaScript ](https://www.bitovi.com/academy/learn-to-debug-javascript.html?hsLang=en-us)
- [ Learn TypeScript ](https://www.bitovi.com/academy/learn-typescript.html?hsLang=en-us)
- [ Learn React Native ](https://www.bitovi.com/academy/learn-react-native.html?hsLang=en-us)
- [ Learn Web Components ](https://www.bitovi.com/academy/learn-web-components.html?hsLang=en-us)
- [ Agile Program Management With Jira ](https://www.bitovi.com/academy/learn-agile-program-management-with-jira.html?hsLang=en-us)
- [ Learn RxJS ](https://www.bitovi.com/academy/learn-rxjs.html?hsLang=en-us)
- [ Giving a Training ](https://www.bitovi.com/academy/training.html?hsLang=en-us)
- [ Learn Technology Consulting ](https://www.bitovi.com/academy/learn-technology-consulting.html?hsLang=en-us)
- [ Learn React ](https://www.bitovi.com/academy/learn-react.html?hsLang=en-us) 
    - [ Introduction to React ](https://www.bitovi.com/academy/learn-react/intro-to-react.html?hsLang=en-us)
    - [ Setting Up Your Environment ](https://www.bitovi.com/academy/learn-react/setting-up-your-environment.html?hsLang=en-us)
    - [ Introduction to JSX ](https://www.bitovi.com/academy/learn-react/intro-to-jsx.html?hsLang=en-us)
    - [ Building Components ](https://www.bitovi.com/academy/learn-react/components.html?hsLang=en-us)
    - [ Passing Props ](https://www.bitovi.com/academy/learn-react/props.html?hsLang=en-us)
    - [ Routing in React ](https://www.bitovi.com/academy/learn-react/routing.html?hsLang=en-us)
    - [ Styling in React ](https://www.bitovi.com/academy/learn-react/styling-in-react.html?hsLang=en-us)
    - [ Managing State in React ](https://www.bitovi.com/academy/learn-react/stateful-hooks.html?hsLang=en-us)
    - [ Making HTTP Requests ](https://www.bitovi.com/academy/learn-react/making-http-requests.html?hsLang=en-us)
    - [ Creating Nested Routes ](https://www.bitovi.com/academy/learn-react/nested-routes.html?hsLang=en-us)
    - [ Handling User Inputs and Forms ](https://www.bitovi.com/academy/learn-react/controlled-vs-uncontrolled.html?hsLang=en-us)
    - [ Testing in React ](https://www.bitovi.com/academy/learn-react/testing.html?hsLang=en-us)
    - [ Additional Resources ](https://www.bitovi.com/academy/learn-react/additional-resources.html?hsLang=en-us)
- [ Learn NgRx ](https://www.bitovi.com/academy/learn-ngrx.html?hsLang=en-us)
- [ Learn Graphql ](https://www.bitovi.com/academy/learn-graphql.html?hsLang=en-us)
- [ Learn Docker ](https://www.bitovi.com/academy/learn-docker.html?hsLang=en-us)
- [ Learn CanJS ](https://www.bitovi.com/academy/learn-canjs.html?hsLang=en-us)
- [ Learn Angular ](https://www.bitovi.com/academy/learn-angular.html?hsLang=en-us)
- [ Advanced TypeScript ](https://www.bitovi.com/academy/advanced-typescript.html?hsLang=en-us)

[View More Courses](https://www.bitovi.com/academy/#courses) [ ]

# Testing in React `page`

Write unit tests to verify components are functioning as expected.

## Overview

In this section, we will:

- Introduce React Testing Library
- Render and verify a component in a test
- Use `@testing-library/user-event` to simulate user interactions

## Objective 1: Write a test for rendering a component and verifying the DOM structure

How do we know when our code is working correctly? How do we know it’s still working correctly in the future after we make changes to it?

Testing helps by verifying that given certain inputs our code generates expected outputs. So far we’ve copied existing tests to prove that we’ve completed the exercise correctly, now let’s dive in and learn about how React testing is done.

The most basic test is to render a component and validate the DOM that is generated. That’s what we’ll do in this first section.

### Introducing React Testing Library

[React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) is a set of tools that helps us write robust tests for our React applications. Unlike some other testing libraries that focus on the internal state and implementation details of components, React Testing Library emphasizes testing the behavior of components as experienced by the end-users. It works with the DOM rendered by React components, allowing tests to cover a wide range of user interactions.

React Testing Library’s north star is:

> The more your tests resemble the way your software is used, the more confidence they can give you.

React Testing Library encourages tests that focus on how the user interacts with the application, rather than the implementation details of the components.

### Rendering and verifying a component in a test

OK, you’re convinced that React Testing Library is a good idea, how do you use it to create tests for your React components?

Let’s take a look at an example: say we want to test a component we created named `EmailInputField` that renders a form field. We expect the component will generate HTML like this:

```
<div class="form-field">
  <label htmlFor="inputId">Email:</label>
  <input id="inputId" type="email" value="test@example.com" />
</div>
```

We want to test `EmailInputField` to make sure it generates the DOM we expect. If you’re already familiar with testing frontend JavaScript code, the following pattern will probably be recognizable: each test consists of arguments to the `it` function provided by Vite. The first argument is a short description of what the test is verifying. The convention is that the description string takes "it" as a prefix and proceeds from there, e.g. "[it] renders the correct label and value."

The second argument to `it` is a callback function that is called to run the test. Inside the callback, invoke the React Testing Library function `render` and pass it a single argument, the JSX for your component including props. After `render` completes, use `screen` to query the DOM and make assertions about the result.

```
it("renders the correct label and value", () => {
  render(<EmailInputField label="Email" value="test@example.com" />)
  const label = screen.getByText("Email:")
  expect(label).toBeInTheDocument()
})
```

In the test above, we validate that the label is correct. We use the `getByText` function to select a single element whose `textContent` matches the string, "Email:". If you look closely you can see that the `<label>` content in the HTML has a ":" (colon) at the end, but the `label` prop does not, we can conclude that EmailInputField appends the colon — but **the purpose of the test isn’t how EmailInputField works, it’s the DOM output that it returns**. After we get an element, we then use `expect` and `toBeInTheDocument` to verify the element was rendered properly. Our test passes because the generated DOM is what we expect the user will perceive.

We also want to validate the `<input>` element; let’s update the test:

```
it("renders the correct label and value", () => {
  render(<EmailInputField label="Email" value="test@example.com" />)
  const label = screen.getByText("Email:")
  expect(label).toBeInTheDocument()

  // Validate the input value.
  const input = screen.getByDisplayValue("test@example.com")
  expect(input).toBeInTheDocument()
})
```

We’ve used a different query to select the `<input>` element: `getByDisplayValue`. It returns an input element whose value matches the provided string. React Testing Library has a [suite of "query" functions](https://testing-library.com/docs/queries/about) that select elements based on characteristics like: role, label text, placeholder text, text, display value, alt text, and title. Our test continues to pass because the input’s value in the DOM matches what we expect.

Before we move on, let’s consider the `type` prop — shouldn’t we test to be sure it was applied properly as the input’s `type` attribute? The answer is maybe. `type="email"` doesn’t affect the appearance of the field in a browser, but it might affect how the user can enter input. For example, a mobile device might display a special on-screen keyboard. For now, we’ll hold off on writing tests that check attribute values and see if there is another, more user-focused way to test this behavior.

### Setup 1

✏️ Create **src/components/FormSelect/FormSelect.tsx** and update it to be:

```
import { useId } from "react"

const FormSelect: React.FC<{
  children: React.ReactNode
  label: string
  onChange: (data: string) => void
  value: string
}> = ({ children, label, onChange, value }) => {
  const id = useId()

  return (
    <div className="form-group">
      <label htmlFor={id} className="control-label">
        {label}:
      </label>

      <select
        className="form-control"
        id={id}
        onChange={(event) => onChange(event.target.value)}
        value={value}
      >
        {children}
      </select>
    </div>
  )
}

export default FormSelect

```

✏️ Create **src/components/FormSelect/index.ts** and update it to be:

```
export { default } from "./FormSelect"

```

✏️ Update **src/pages/RestaurantList/RestaurantList.tsx** to be:

```
import { useState, useCallback } from "react"
import {
  useCities,
  useRestaurants,
  useStates,
} from "../../services/pmo/restaurant"
import ListItem from "./ListItem"
import FormSelect from "../../components/FormSelect"

const RestaurantList: React.FC = () => {
  const [state, setState] = useState("")
  const [city, setCity] = useState("")

  const statesResponse = useStates()

  const citiesResponse = useCities(state)

  const restaurantsResponse = useRestaurants(state, city)

  const updateState = (stateShortCode: string) => {
    setState(stateShortCode)
    setCity("")
  }

  const updateCity = (cityName: string) => {
    setCity(cityName)
  }

  const chooseStatePlaceholder = useCallback(()=>{
    let placeholderText = "Choose a state";

    if(statesResponse.isPending) {
      placeholderText = "Loading states…";
    } else if (statesResponse.error) {
      placeholderText = statesResponse.error.message
    }

    return placeholderText;
  }, [statesResponse])

  const chooseCityPlaceholder = useCallback(()=>{
    let placeholderText = "Choose a city";

    if(!state) {
      placeholderText = "Choose a state before selecting a city";
    } else if (citiesResponse.isPending) {
      placeholderText = "Loading cities…";
    } else if (citiesResponse.error) {
      placeholderText = citiesResponse.error.message;
    } 

    return placeholderText;
  }, [state, citiesResponse])

  return (
    <>
      <div className="restaurants">
        <h2 className="page-header">Restaurants</h2>

        <form className="form">
          <FormSelect label="State" onChange={updateState} value={state}>
            <option key="choose_state" value="">
                {chooseStatePlaceholder()}
            </option>
            {statesResponse.data?.map(({ short, name }) => (
              <option key={short} value={short}>
                {name}
              </option>
            ))}
          </FormSelect>

          <FormSelect label="City" onChange={updateCity} value={city}>
            <option key="choose_city" value="">
              {chooseCityPlaceholder()}
            </option>
            {state &&
              citiesResponse.data?.map(({ name }) => (
                <option key={name} value={name}>
                  {name}
                </option>
              ))}
          </FormSelect>
        </form>

        {city && restaurantsResponse.error && (
          <p aria-live="polite" className="restaurant">
            Error loading restaurants: {restaurantsResponse.error.message}
          </p>
        )}

        {city && restaurantsResponse.isPending && (
          <p aria-live="polite" className="restaurant loading">
            Loading restaurants…
          </p>
        )}

        {city &&
          restaurantsResponse.data &&
          (restaurantsResponse.data.length === 0
            ? !restaurantsResponse.isPending && (
                <p aria-live="polite">No restaurants found.</p>
              )
            : restaurantsResponse.data.map(
                ({ _id, slug, name, address, images }) => (
                  <ListItem
                    key={_id}
                    address={address}
                    name={name}
                    slug={slug}
                    thumbnail={images.thumbnail}
                  />
                ),
              ))}
      </div>
    </>
  )
}

export default RestaurantList

```

### Verify 1

✏️ Create **src/components/FormSelect/FormSelect.test.tsx** and update it to be:

```
import "@testing-library/jest-dom"
import { render, screen } from "@testing-library/react"
import { describe, expect, it } from "vitest"
import FormSelect from "./FormSelect"

describe("FormSelect Component", () => {
  it("renders correctly with given props", () => {
    throw new Error("Test needs to be implemented")
  })
})

```

### Exercise 1

- Call `render()` with the JSX for the component you want to test.
- Call `screen.getByText()` to get the `<label>` element.
- Use `.toBeInTheDocument()` to check whether an element is in the DOM.
- Call `screen.getByRole()` to get other elements.

Hint: Here’s the JSX you can use for the component:

```
const content = (
  <FormSelect label="Test Label" onChange={() => {}} value="">
    <option value="option1">Option 1</option>
    <option value="option2">Option 2</option>
  </FormSelect>
)
```

**Having issues with your local setup?** You can use either [StackBlitz](https://stackblitz.com/fork/github/bitovi/academy/tree/main/exercises/react-vite/12-testing/01-problem?file=src/components/FormSelect/FormSelect.test.tsx) or [CodeSandbox](https://codesandbox.io/p/devbox/github/bitovi/academy/tree/main/exercises/react-vite/12-testing/01-problem?file=src/components/FormSelect/FormSelect.test.tsx) to do this exercise in an online code editor.

### Solution 1

Click to see the solution

✏️ Update **src/components/FormSelect/FormSelect.test.tsx** to be:

```
import "@testing-library/jest-dom"
import { render, screen } from "@testing-library/react"
import { describe, expect, it } from "vitest"
import FormSelect from "./FormSelect"

describe("FormSelect Component", () => {
  it("renders correctly with given props", () => {
    render(
      <FormSelect label="Test Label" onChange={() => {}} value="">
        <option value="option1">Option 1</option>
        <option value="option2">Option 2</option>
      </FormSelect>,
    )

    // Check if label is rendered correctly
    expect(screen.getByText(/test label/i)).toBeInTheDocument()

    // Check if select options are rendered
    expect(screen.getByRole("option", { name: "Option 1" })).toBeInTheDocument()
    expect(screen.getByRole("option", { name: "Option 2" })).toBeInTheDocument()
  })
})

```

**Having issues with your local setup?** See the solution in [StackBlitz](https://stackblitz.com/fork/github/bitovi/academy/tree/main/exercises/react-vite/12-testing/01-solution?file=src/components/FormSelect/FormSelect.test.tsx) or [CodeSandbox](https://codesandbox.io/p/devbox/github/bitovi/academy/tree/main/exercises/react-vite/12-testing/01-solution?file=src/components/FormSelect/FormSelect.test.tsx).

## Objective 2: Write a test for handling user interactions

Most components can respond to events raised by user interaction, like clicking a button. How can we test code that responds to these interactions? We use another React Testing Library package named [user-event](https://testing-library.com/docs/user-event/intro).

`user-event` allows your tests to interact with your component in a browser-like manner, where a single user action can raise multiple events. For example, when a button is clicked, it can emit a focus event and then a click event. `user-event` also has some helpful functionality to prevent interactions that are not possible in a browser environment, such as not firing a click event from an element that’s hidden.

All of the methods provided by `user-event` return a Promise, so we want to prefix the test callback function with `async` and use `await` when a `user-event` method is called.

### Using `@testing-library/user-event` to simulate user interactions

Referring back to the HTML output by the `EmailInputField` component:

```
<div class="form-field">
  <label htmlFor="inputId">Email:</label>
  <input id="inputId" type="email" value="test@example.com" />
</div>
```

We want to write a test to verify that what the user types is displayed as the `<input>`’s value. The user-event library includes a method named `keyboard` that we can use to send a sequence of key events to the `<input>` element. The `keyboard` method provides a good simulation of what happens when a user is typing. Before we use `keyboard` in a test, we need to [initialize user events with the `setup` method](https://testing-library.com/docs/user-event/setup#direct-apis). Let’s look at the test:

```
import userEvent from "@testing-library/user-event"

it("captures email input", async () => {
  const user = userEvent.setup()

  render(<EmailInputField label="Email" value="" />)

  const input = screen.getByLabelText("Email:")
  expect(input).toBeInTheDocument()
  expect(input).toHaveDisplayValue("") // Verify the beginning state: no value is set.

  await user.click(input)
  await user.keyboard("test@example.com")
  expect(input).toHaveDisplayValue("test@example.com")
})
```

There are some notable differences compared to the previous test:

- The userEvent module is imported.
- A `user` is created with the `userEvent.setup()` function.
- The callback function argument of `it` is prefaced with `async`.
- We need to `await` both the `click`, and `keyboard` methods to let them complete.
- The input element needs to have focus before we "type" input; rather than using the input element’s `focus` method, we prefer the more performant `user.click()` method.

After `keyboard` completes, we can make assertions about the current state of the input, including the current value, which should have been updated with the value of the single `keyboard` argument.

There is one final point to consider: testing the `type` attribute of the `<input>` element. We'd prefer to follow React Testing Library's philosophy of testing the DOM generated by a component. To put this into practice, we need some knowledge about different `<input>` type values, their effects on the user experience, and why we might choose one over another. Let’s review what MDN has to say about the ["email" type value](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email):

> The input value is automatically validated to ensure that it’s either empty or a properly-formatted email address (or list of addresses) before the form can be submitted. The :valid and :invalid CSS pseudo-classes are automatically applied as appropriate to visually denote whether the current value of the field is a valid email address or not.

This is a great reason for choosing the "email" type rather than the "text" type. The "email" type prevents the user from entering an incorrectly formatted email address. This also helps guide our thinking — rather than testing an attribute **value**, we can test the input’s **behavior**. We’ll add another test to ensure incorrect email formats are flagged as invalid.

```
import userEvent from "@testing-library/user-event"

it("flags an incorrectly formatted email address as invalid", async () => {
  const user = userEvent.setup()

  render(<EmailInputField label="Email" value="" />)

  const input = screen.getByLabelText("Email:")
  expect(input).toBeInTheDocument()

  await user.click(input)
  await user.keyboard("test")
  expect(input).toHaveDisplayValue("test")

  await user.tab()
  expect(input).not.toHaveFocus()
  expect(input).toBeInvalid()
})
```

Compared to the prior test, this one inputs a string that is not a valid email address and has three additional lines at the end that move focus away from the `<input>` element, triggering the built-in validation to be executed, and then asserts that the `<input>` has been [marked as invalid](https://github.com/testing-library/jest-dom?tab=readme-ov-file#tobeinvalid).

### Verify 2

✏️ Update **src/components/FormSelect/FormSelect.test.tsx** to be:

```
import "@testing-library/jest-dom"
import { render, screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { describe, expect, it, vi } from "vitest"
import FormSelect from "./FormSelect"

describe("FormSelect Component", () => {
  it("renders correctly with given props", () => {
    render(
      <FormSelect label="Test Label" onChange={() => {}} value="">
        <option value="option1">Option 1</option>
        <option value="option2">Option 2</option>
      </FormSelect>,
    )

    // Check if label is rendered correctly
    expect(screen.getByText(/test label/i)).toBeInTheDocument()

    // Check if select options are rendered
    expect(screen.getByRole("option", { name: "Option 1" })).toBeInTheDocument()
    expect(screen.getByRole("option", { name: "Option 2" })).toBeInTheDocument()
  })

  it("calls onChange with the correct value when selection changes", async () => {
    throw new Error("Test needs to be implemented")
  })
})

```

### Exercise 2

- Call `vi.fn()` to create a function you can observe.
- Call `userEvent.setup()` to set up the object for simulating user interactions.
- Call `await user.selectOptions()` to simulate selecting an option in a `<select>` element.
- Use `.toHaveBeenCalledWith()` to confirm whether the `onChange` handler is called.

**Having issues with your local setup?** You can use either [StackBlitz](https://stackblitz.com/fork/github/bitovi/academy/tree/main/exercises/react-vite/12-testing/02-problem?file=src/components/FormSelect/FormSelect.test.tsx) or [CodeSandbox](https://codesandbox.io/p/devbox/github/bitovi/academy/tree/main/exercises/react-vite/12-testing/02-problem?file=src/components/FormSelect/FormSelect.test.tsx) to do this exercise in an online code editor.

### Solution 2

Click to see the solution

✏️ Update **src/components/FormSelect/FormSelect.test.tsx** to be:

```
import "@testing-library/jest-dom"
import { render, screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { describe, expect, it, vi } from "vitest"
import FormSelect from "./FormSelect"

describe("FormSelect Component", () => {
  it("renders correctly with given props", () => {
    render(
      <FormSelect label="Test Label" onChange={() => {}} value="">
        <option value="option1">Option 1</option>
        <option value="option2">Option 2</option>
      </FormSelect>,
    )

    // Check if label is rendered correctly
    expect(screen.getByText(/test label/i)).toBeInTheDocument()

    // Check if select options are rendered
    expect(screen.getByRole("option", { name: "Option 1" })).toBeInTheDocument()
    expect(screen.getByRole("option", { name: "Option 2" })).toBeInTheDocument()
  })

  it("calls onChange with the correct value when selection changes", async () => {
    const handleChange = vi.fn()
    const user = userEvent.setup()

    render(
      <FormSelect label="Test Label" onChange={handleChange} value="option1">
        <option value="option1">Option 1</option>
        <option value="option2">Option 2</option>
      </FormSelect>,
    )

    // Simulate user changing the selection
    await user.selectOptions(screen.getByRole("combobox"), "option2")

    // Check if onChange was called with the right value
    expect(handleChange).toHaveBeenCalledWith("option2")
  })
})

```

**Having issues with your local setup?** See the solution in [StackBlitz](https://stackblitz.com/fork/github/bitovi/academy/tree/main/exercises/react-vite/12-testing/02-solution?file=src/components/FormSelect/FormSelect.test.tsx) or [CodeSandbox](https://codesandbox.io/p/devbox/github/bitovi/academy/tree/main/exercises/react-vite/12-testing/02-solution?file=src/components/FormSelect/FormSelect.test.tsx).

## Next steps

Congrats, you’ve completed this Bitovi Academy training!

To continue your learning journey, be sure to check out our [Additional Resources](https://www.bitovi.com/academy/learn-react/additional-resources.html?hsLang=en-us) page for further reading, hands-on exercises, and helpful guides.

###### On this page...

###### Get help

If you need [help](https://discord.gg/J7ejFsZnJ4) with an exercise, please reach out to us on [Discord](https://discord.gg/J7ejFsZnJ4)

If you find a bug, please [create an issue](https://github.com/bitovi/academy/issues/new) or email [contact@bitovi.com](mailto:contact@bitovi.com?Subject=Training%20Suggestion)

## Join the Bitovi Newsletter

 Do you want to receive updates about new Bitovi Academy courses, Bitovi open source projects, and dev community news? If so, please provide your email. We won’t spam you or give it away for any reason!  
*By providing your email you agree to receive communications from Bitovi*

![](https://bitovi.github.io/academy/static/img/close.svg)

```json
{
     "@context": "http://schema.org",
     "@type": "Organization",
     "mainEntityOfPage":{
          "@type":"WebPage",
          "@id":"https://www.bitovi.com/academy/learn-react/testing.html",
          "description": "Write unit tests to verify components are functioning as expected. 
"
     },
     "url": "http://bitovi.com",
     "logo": "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg",
     "image": "https://www.bitovi.com/hubfs/bitovi-logo-x2.png",
     "name": "Bitovi Web App Consulting",
     "alternateName": "Bitovi",
      "naics": "541511",
      "description": "Bitovi is a UX, UI design and front-end JavaScript development consulting company",
     "address": {
          "@type": "PostalAddress",
          "streetAddress": "1134 Pine Tree Lane ",
          "addressLocality": "Libertyville",
          "addressRegion": "IL",
          "addressCountry": "United States",
          "postalCode": "60048"
     },
     "areaServed": {
          "@type": "GeoCircle",
          "geoMidpoint": {
               "@type": "GeoCoordinates",
               "latitude": "41.8781",
               "longitude": "87.6298"
          },
          "geoRadius": "5000 km"
     },
     "telephone": "312-620-0386",
     "email": "contact@bitovi.com",
     "sameAs": [
          "https://www.facebook.com/BitoviLLC/",
          "https://twitter.com/bitovi",
          "https://www.linkedin.com/company/bitovi"
     ]
}
```