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
呼叫以在元件的上下文中測試 hooks (如果您的 hook 不需要元件的上下文,則 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;
}
這可以用於輕鬆測試 hook/primitive
const {result} = renderHook(createResult)
expect(result).toBe(true)
如果您正在使用帶有 renderHook
的 wrapper
,請確保它始終回傳 props.children
- 特別是如果您正在使用帶有非同步程式碼的上下文以及 <Show>
,因為這是從 hook 獲取值所必需的,並且只會同步獲得一次,否則您只會得到 undefined
並想知道為什麼會這樣。
renderDirective
Solid.js 支援 自訂 directives,這是一種方便的模式,可以將自訂行為繫結到元素,因此我們也有 renderDirective
呼叫,它擴充了 renderHook
以接受一個 directive 作為第一個引數,接受引數的 initialValue
以及 options
中的 targetElement
(字串、HTMLElement 或回傳 HTMLElement 的函數),並且還回傳 arg
和 setArg
以讀取和操縱 directive 的引數。
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> };
這允許非常有效且簡潔的 directives 測試
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
輔助程式,它補充了指令和 hooks 的 hooks。
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
時非常有用,它在結果中給出所有者欄位。回傳值是一個 Promise,其中包含給定給 done()
回呼的值。您可以等待結果以進行進一步的斷言,或將其回傳給您的測試執行程式。
已知問題
如果您正在使用 vitest
,則測試可能會失敗,因為套件 solid-js
和 @solidjs/router
(如果使用)只需要載入一次,它們可以通過內部的 vite
伺服器和通過節點載入。因此發生的典型錯誤是 dispose 應該是 undefined,或者路由器無法載入。
自 2.8.2 版以來,我們的 vite 外掛程式已獲得配置所有測試的功能,因此您只需要對全域、覆蓋率等進行額外配置。