Skip to content

Testing

Fresh

Like with every other application, testing is an important factor when releasing into the wild. React Three Fiber can be tested using @react-three/test-renderer.

Installation

bash
npm install @react-three/test-renderer --save-dev

React Three Test Renderer is testing library agnostic — it works with jest, jasmine, etc.

Basic Setup

jsx
import ReactThreeTestRenderer from '@react-three/test-renderer'
import { MyRotatingBox } from './App'

test('mesh to have two children', async () => {
  const renderer = await ReactThreeTestRenderer.create(<MyRotatingBox />)
})

test('click event makes box bigger', async () => {
  const renderer = await ReactThreeTestRenderer.create(<MyRotatingBox />)
})

Testing Scene Structure

Get the scene and its children from the test instance:

js
const meshChildren = renderer.scene.children

Use allChildren to get geometry and materials (not just groups):

js
const meshChildren = renderer.scene.children[0].allChildren

INFO

children is meant for groups and does not return geometry and materials. Use allChildren to get all children including geometry and materials.

Assert on the structure:

js
expect(meshChildren.length).toBe(2)

Full test:

js
test('mesh to have two children', async () => {
  const renderer = await ReactThreeTestRenderer.create(<MyRotatingBox />)
  const mesh = renderer.scene.children[0].allChildren
  expect(mesh.length).toBe(2)
})

Testing Interactions

Use the fireEvent method on a test instance to simulate user events:

js
const mesh = renderer.scene.children[0]
await renderer.fireEvent(mesh, 'click')

Then assert on the resulting state:

js
expect(mesh.props.scale).toBe(1.5)

Full interaction test:

js
test('click event makes box bigger', async () => {
  const renderer = await ReactThreeTestRenderer.create(<MyRotatingBox />)
  const mesh = renderer.scene.children[0]
  expect(mesh.props.scale).toBe(1)
  await renderer.fireEvent(mesh, 'click')
  expect(mesh.props.scale).toBe(1.5)
})

Testing with act (v9+)

In R3F v9, act is exported from React itself:

tsx
import { act } from 'react'
import { createRoot } from '@react-three/fiber'

const store = await act(async () => createRoot(canvas).render(<App />))
console.log(store.getState())

StrictMode in Tests (v9)

StrictMode is now correctly inherited from a parent renderer. Previously it had to be redeclared within the canvas:

diff
<StrictMode>
  <Canvas>
-    <StrictMode>
-      // ...
-    </StrictMode>
+    // ...
  </Canvas>
</StrictMode>

Exercises

  • Check the color of a mesh: access mesh.props.material-color or traverse allChildren
  • Check rotation using the advanceFrames method on the renderer
  • Test loading states with Suspense fallbacks