API
由于受到 preact-testing-library 的启发,您可以查看其页面以了解更多信息。
需要注意的是,存在一些关键差异。
render
render
函数接受一个返回 Solid 组件的函数,而不是仅仅返回组件本身。
const results = render(() => <YourComponent />, options)
Solid.js 不 重新渲染,它仅仅执行由改变 DOM 的响应式状态触发的副作用,因此没有 rerender
方法。您可以使用全局信号以一种导致测试组件更新的方式操作它。
除了原始 API 之外,此测试库的 render 函数还支持一个方便的 location
选项,它将设置一个指向指定位置的内存路由器。由于此设置不是同步的,因此在使用它之后,您需要首先使用异步查询(findBy
)。
it('uses params', async () => {
const App = () => (
<>
<Route path="/ids/:id" component={() => <p>Id: {useParams()?.id}</p>} />
<Route path="/" component={() => <p>Start</p>} />
</>
)
const {findByText} = render(() => <App />, {location: 'ids/1234'})
expect(await findByText('Id: 1234')).not.toBeFalsy()
})
它使用 @solidjs/router
,因此如果您想使用不同的路由器,则应考虑使用 wrapper
选项。如果您尝试在没有安装该包的情况下使用它,您将收到一条错误消息。
renderHook
Solid.js 的外部响应式状态不需要任何 DOM 元素就能运行,因此我们的 renderHook
调用在组件上下文中测试钩子(如果您的钩子不需要组件上下文,createRoot
就足以测试响应式行为;为了方便起见,我们还有 createEffect
,它在 异步方法
部分有描述)没有 container
、baseElement
或查询在其选项或返回值中。相反,它有一个 owner
,用于与 runWithOwner
一起使用(如果需要)。它还公开了一个 cleanup
函数,但该函数在测试结束后会自动调用。
function renderHook<Args extends any[], Result>(
hook: (...args: Args) => Result,
options: {
initialProps?: Args,
wrapper?: Component<{ children: JSX.Element }>
}
) => {
result: Result;
owner: Owner | null;
cleanup: () => void;
}
这可用于轻松测试钩子/原语。
const {result} = renderHook(createResult)
expect(result).toBe(true)
如果您在 renderHook
中使用 wrapper
,请确保它将 始终 返回 props.children
- 特别是如果您在使用上下文和异步代码以及 <Show>
的情况下,因为这是从钩子获取值的必要条件,并且它只在同步情况下获取一次,否则您只会得到 undefined
并且想知道为什么会出现这种情况。
renderDirective
Solid.js 支持 自定义指令,这是一种将自定义行为绑定到元素的方便模式,因此我们还有一个 renderDirective
调用,它增强了 renderHook
,使其接受指令作为第一个参数,接受一个用于参数的 initialValue
和一个 targetElement
(字符串、HTMLElement 或返回 HTMLElement 的函数)作为 options
,并且还返回 arg
和 setArg
以读取和操作指令的参数。
function renderDirective<
Arg extends any,
Elem extends HTMLElement
>(
directive: (ref: Elem, arg: Accessor<Arg>) => void,
options?: {
...renderOptions,
initialValue: Arg,
targetElement:
| Lowercase<Elem['nodeName']>
| Elem
| (() => Elem)
}
): Result & { arg: Accessor<Arg>, setArg: Setter<Arg> };
这允许非常有效和简洁地测试指令。
const {asFragment, setArg} = renderDirective(myDirective)
expect(asFragment()).toBe('<div data-directive="works"></div>')
setArg('perfect')
expect(asFragment()).toBe('<div data-directive="perfect"></div>')
异步方法
Solid.js 响应式更改非常快,因此很少需要使用 waitFor(…)
、await findByRole(…)
和其他异步查询来测试渲染结果,除了过渡、悬念、资源和路由导航。
Solid.js 使用 createEffect
的不同变体来管理副作用。虽然您可以使用 waitFor
来测试异步效果,但它使用轮询而不是允许 Solid 的响应式性触发下一步。为了简化测试这些异步效果,我们有一个 testEffect
帮助程序,它补充了用于指令和钩子的钩子。
testEffect(fn: (done: (result: T) => void) => void, owner?: Owner): Promise<T>
// use it like this:
test("testEffect allows testing an effect asynchronously", () => {
const [value, setValue] = createSignal(0);
return testEffect(done => createEffect((run: number = 0) => {
if (run === 0) {
expect(value()).toBe(0);
setValue(1);
} else if (run === 1) {
expect(value()).toBe(1);
done();
}
return run + 1;
}));
});
它允许在定义的拥有者(作为可选的第二个参数接收)中运行效果。这与 renderHook
结合使用可能很有用,renderHook
在其结果中为您提供了拥有者字段。返回值是一个包含传递给 done()
回调的值的 Promise。您可以等待结果以进行进一步断言,也可以将其返回给您的测试运行器。
已知问题
如果您使用 vitest
,那么测试可能会失败,因为包 solid-js
和 @solidjs/router
(如果使用)只需要加载一次,并且它们可能会通过内部 vite
服务器和通过节点加载。由于此原因而发生的典型错误是,dispose 被认为是未定义的,或者无法加载路由器。
从 2.8.2 版本开始,我们的 vite 插件获得了配置测试所有内容的功能,因此您只需要为全局变量、覆盖率等进行额外的配置。