跳至主要内容

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,它在 异步方法 部分有描述)没有 containerbaseElement 或查询在其选项或返回值中。相反,它有一个 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,并且还返回 argsetArg 以读取和操作指令的参数。

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 插件获得了配置测试所有内容的功能,因此您只需要为全局变量、覆盖率等进行额外的配置。