Expo Router 的测试配置

编辑页面

了解在使用 Expo Router 时如何为你的应用创建集成测试。


For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.

Expo Router 依赖你的文件系统,这在为集成测试设置 mock 时可能会带来挑战。Expo Router 的子模块 expo-router/testing-library 是一组建立在广受欢迎的 @testing-library/react-native 之上的测试工具,它允许你快速创建已预先配置好用于测试的、基于内存的 Expo Router 应用。

配置

在继续之前,请确保你已经在项目中根据 使用 Jest 进行单元测试@testing-library/react-native 设置好了 jest-expo

注意:使用 Expo Router 时,不要把测试文件放在 app 目录中。你 app 目录中的所有文件都必须是路由文件或布局文件。请改用 __tests__ 目录或单独的目录。这种方法在 使用 Jest 进行单元测试 中有说明。

renderRouter

renderRouter 扩展了 render 的功能,以简化 Expo Router 的测试。它返回与 render 相同的查询对象,并且兼容 screen,让你可以使用标准的 查询 API 来定位组件。

renderRouter 接受与 render 相同的 选项,并额外引入了一个 initialUrl 选项,用于设置初始路由,以模拟深度链接。

内联文件系统

renderRouter(mock: Record<string, ReactComponent>, options: RenderOptions)

renderRouter 可以通过将对象作为第一个参数传入该函数,来提供文件系统的内联 mock。对象的键是 mock 文件系统路径。定义这些路径时不要使用以 ./ 开头的相对路径或 / 开头的绝对路径,并且要去掉文件扩展名。

app.test.tsx
import { renderRouter, screen } from 'expo-router/testing-library'; import { View } from 'react-native'; it('my-test', async () => { const MockComponent = jest.fn(() => <View />); renderRouter( { index: MockComponent, 'directory/a': MockComponent, '(group)/b': MockComponent, }, { initialUrl: '/directory/a', } ); expect(screen).toHavePathname('/directory/a'); });

带有 `null` 组件的内联文件系统

renderRouter(mock: string[], options: RenderOptions)

renderRouter 提供一个字符串数组会创建一个带有 null 组件({ default: () => null })的内联 mock 文件系统。这适用于你不需要测试某个路由输出的场景。

app.test.tsx
import { renderRouter, screen } from 'expo-router/testing-library'; it('my-test', async () => { renderRouter(['index', 'directory/a', '(group)/b'], { initialUrl: '/directory/a', }); expect(screen).toHavePathname('/directory/a'); });

fixture 路径

renderRouter(fixturePath: string, options: RenderOptions)

renderRouter 可以接受一个目录路径来 mock 现有的 fixture。请确保提供的路径是相对于当前测试文件的。

app.test.tsx
import { renderRouter } from 'expo-router/testing-library'; import { View } from 'react-native'; it('my-test', async () => { const MockComponent = jest.fn(() => <View />); renderRouter('./my-test-fixture'); });

带覆盖项的 fixture 路径

renderRouter({ appDir: string, overrides: Record<string, ReactComponent>}, options: RenderOptions)

对于更复杂的测试场景,renderRouter 可以同时利用目录路径和内联 mock 方法。appDir 参数接受一个表示目录路径的字符串。overrides 参数是一个内联 mock,可用于覆盖 appDir 中的特定路径。这种组合可以让你对 mock 环境进行精细控制。

app.test.tsx
import { renderRouter } from 'expo-router/testing-library'; import { View } from 'react-native'; it('my-test', async () => { const MockAuthLayout = jest.fn(() => <View />); renderRouter({ appDir: './my-test-fixture', overrides: { 'directory/(auth)/_layout': MockAuthLayout, }, }); });

Jest 匹配器

以下匹配器已添加到 expect,可用于在 screen 上断言值。

toHavePathname()

断言当前 pathname 与给定字符串一致。该匹配器使用当前 screenusePathname hook 的值。

app.test.tsx
expect(screen).toHavePathname('/my-router');

toHavePathnameWithParams()

断言当前 pathname(包括 URL 参数)与给定字符串一致。这对于断言网页浏览器中 URL 的显示很有用。

app.test.tsx
expect(screen).toHavePathnameWithParams('/my-router?hello=world');

toHaveSegments()

断言当前 segments 与一个字符串数组一致。该匹配器使用当前 screenuseSegments hook 的值。

app.test.tsx
expect(screen).toHaveSegments(['[id]']);

useLocalSearchParams()

断言当前局部 URL 参数与一个对象一致。该匹配器使用当前 screenuseLocalSearchParams hook 的值。

app.test.tsx
expect(screen).useLocalSearchParams({ first: 'abc' });

useGlobalSearchParams()

断言当前屏幕的 pathname 与某个值匹配。使用 useGlobalSearchParams hook 的值进行比较。

断言当前全局 URL 参数与一个对象一致。该匹配器使用当前 screenuseGlobalSearchParams hook 的值。

app.test.tsx
expect(screen).useGlobalSearchParams({ first: 'abc' });

toHaveRouterState()

一个高级匹配器,用于将当前路由状态与一个对象进行断言。

app.test.tsx
expect(screen).toHaveRouterState({ routes: [{ name: 'index', path: '/' }], });