---
meta:
  title: Testing | React Spring
  'og:title': Testing | React Spring
  'twitter:title': Testing | React Spring
  description: A detailed guide to using React Spring with testing.
  'og:description': A detailed guide to using React Spring with testing.
  'twitter:description': A detailed guide to using React Spring with testing.
  'og:url': https://www.react-spring.dev/docs/guides/testing
  'twitter:url': https://www.react-spring.dev/docs/guides/testing
sidebar_position: 2
---

import { formatFrontmatterToRemixMeta } from '../helpers/meta'

export const meta = formatFrontmatterToRemixMeta(frontmatter)

# Testing

:::note
All the examples in this guide are using [`jest`](https://jestjs.io/) as the testing framework & [`react-testing-library`](https://testing-library.com/docs/react-testing-library/intro/).
If you wish to see these examples using other testing frameworks or perhaps there are caveats
that are specific to another testing framework, please open an issue or a PR.
:::

## Introduction

In this guide, we'll go through testing a basic component that uses `useSpring` and `animated` from the library:

```tsx
const FadeIn = ({ isVisible, children }) => {
  const styles = useSpring({
    opacity: isVisible ? 1 : 0,
    y: isVisible ? 0 : 24,
  })

  return <animated.div style={styles}>{children}</animated.div>
}
```

Our component takes two props, `children` & more importantly, `isVisible`. When `isVisible` is `true`, we want to fade
in the component and when it's `false`, we want to fade it out. We could test the `y` position of the element, but for
the purposes of this guide, we'll focus on the `opacity`.

So, let's start by writing a test for this component.

## Example

```tsx
import { render, screen } from '@testing-library/react'
import { animated, useSpring } from '@react-spring/web'

import { FadeIn } from './FadeIn'

test('Correctly renders the FadeIn component', async () => {
  const { rerender } = render(<FadeIn>Hello!</FadeIn>)

  const element = screen.getByText('Hello!')

  expect(element).toHaveStyle('opacity: 0')
})
```

This initial test is pretty simple, we're just rendering the component and asserting that the opacity is `0` when the
component is initially mounted, and by all means it will be pass. Now we want to look at testing that the opacity of element
changes when the `isVisible` prop changes. So we modify our test to look like this:

```tsx lines=13-15
import { render, screen } from '@testing-library/react'
import { animated, useSpring } from '@react-spring/web'

import { FadeIn } from './FadeIn'

test('Correctly renders the FadeIn component', () => {
  const { rerender } = render(<FadeIn>Hello!</FadeIn>)

  const element = screen.getByText('Hello!')

  expect(element).toHaveStyle('opacity: 0')

  rerender(<FadeIn isVisible>Hello!</FadeIn>)

  expect(element).toHaveStyle('opacity: 1')
})
```

This test will fail with an error that will look something like this:

```bash
expect(element).toHaveStyle()

- Expected

- opacity: 1;
+ opacity: 0;

  21 |   rerender(<FadeIn isVisible>Hello!</FadeIn>);
> 23 |   expect(element).toHaveStyle("opacity: 1");
  24 | });
```

And if you're familiar with how `jest` works you won't be surprised by this error. The problem is that `useSpring` animates
values, that is the value isn't set immediately, but rather it's changed over time. So, we could wait for the animation to
become what we expect by using `waitFor`:

```tsx lines=1,6,15-17
import { render, screen, waitFor } from '@testing-library/react'
import { animated, useSpring } from '@react-spring/web'

import { FadeIn } from './FadeIn'

test('Correctly renders the FadeIn component', async () => {
  const { rerender } = render(<FadeIn>Hello!</FadeIn>)

  const element = screen.getByText('Hello!')

  expect(element).toHaveStyle('opacity: 0')

  rerender(<FadeIn isVisible>Hello!</FadeIn>)

  await waitFor(() => {
    expect(element).toHaveStyle('opacity: 1')
  })
})
```

And this would pass, but now we're waiting for the animation to change and update and this adds more time to your tests, time
that's a bit unnecessary because we're not interested in the visual effects in this scenario, we want to know the updates are
correctly made.

## Skipping Animations

The solution to this problem is to skip animations when testing. This can be done by using the `Globals` object and calling the `assign`
method setting `skipAnimations` to `true`. You can do this immediately in the `setup` file for your tests or if you want more granual
control then you could use the `beforeAll | beforeEach` hooks to set it.

```tsx lines=2,5-10
import { render, screen, waitFor } from '@testing-library/react'
import { animated, useSpring, Globals } from '@react-spring/web'

import { FadeIn } from './FadeIn'

beforeAll(() => {
  Globals.assign({
    skipAnimation: true,
  })
})

test('Correctly renders the FadeIn component', async () => {
  const { rerender } = render(<FadeIn>Hello!</FadeIn>)

  const element = screen.getByText('Hello!')

  expect(element).toHaveStyle('opacity: 0')

  rerender(<FadeIn isVisible>Hello!</FadeIn>)

  await waitFor(() => {
    expect(element).toHaveStyle('opacity: 1')
  })
})
```

This would then set the opacity immediate to `1` and the test would pass. However, we still are required to use `waitFor` because the update
requires a tick of the environment to reflect the changes.

## Fake Timers

Alternatively, if you want to keep your code simpler by avoiding an async call for `waitFor` you could opt to use `jest.useFakeTimers` and manually advance the environment:

```tsx lines=11,23
import { render, screen, waitFor } from '@testing-library/react'
import { animated, useSpring, Globals } from '@react-spring/web'

import { FadeIn } from './FadeIn'

beforeAll(() => {
  Globals.assign({
    skipAnimation: true,
  })

  jest.useFakeTimers()
})

test('Correctly renders the FadeIn component', async () => {
  const { rerender } = render(<FadeIn>Hello!</FadeIn>)

  const element = screen.getByText('Hello!')

  expect(element).toHaveStyle('opacity: 0')

  rerender(<FadeIn isVisible>Hello!</FadeIn>)

  jest.advanceTimersByTime(1)

  expect(element).toHaveStyle('opacity: 1')
})
```

## Troubleshooting

### ESM modules not handled by jest

```bash
path/to/project/node_modules/@react-spring/web/react-spring-web.esm.js.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/esm/objectWithoutPropertiesLoose';
```

You may have come across this message when testing with `jest`. If you have, this is because jest
is incorrectly resolving the correct file type for the library. It in fact wants to be using the
`cjs` file type. To fix this, you can add the following to your `jest.config.js` file:

```ts
module.exports = {
  moduleNameMapper: {
    '@react-spring/web':
      '<rootDir>/node_modules/@react-spring/web/react-spring-web.cjs.js',
  },
}
```

This could be applicable to any target you're using e.g. `@react-spring/native`.
