API
Marko Testing Library
は、DOM Testing Library
のすべての機能に加えて、以下のメソッドを提供します。
render
function render(
template, // A Marko template to render
input, // Input for the above template
options // You won't often use this, expand below for docs on options
)
document.body
に追加されるコンテナ内にレンダリングします。
import {render} from '@marko/testing-library'
import MyTemplate from './my-template.marko'
render(MyTemplate)
import {render, screen} from '@marko/testing-library'
import Greeting from './greeting.marko'
test('renders a message', async () => {
const {container} = await render(Greeting, {name: 'Marko'})
expect(screen.getByText(/Marko/)).toBeInTheDocument()
expect(container.firstChild).toMatchInlineSnapshot(`
<h1>Hello, Marko!</h1>
`)
})
render
オプション
オプションを指定する必要はほとんどありませんが、必要な場合は、render
の第三引数として提供できるオプションを以下に示します。
container
クライアントサイドテストではデフォルトで、Marko Testing Library
は div
を作成し、その div
を document.body
に追加します。ここにコンポーネントがレンダリングされます。このオプションを介して独自の HTMLElement container
を提供する場合は、document.body
に自動的に追加されません。
例:tablebody
要素の単体テストを行う場合、div
の子要素にすることはできません。この場合、レンダリングcontainer
としてtable
を指定できます。
const table = document.createElement('table')
const {container} = await render(MyTableBody, null, {
container: document.body.appendChild(table),
})
render
の結果
render
メソッドは、いくつかのプロパティを持つオブジェクトを解決するPromiseを返します。
...queries
render
の最も重要な機能は、コアAPI のクエリが、コンポーネントのレンダリング結果に最初の引数をバインドして自動的に返されることです。
クエリ を参照して、完全なリストを確認してください。
例
const {getByLabelText, queryAllByTestId} = await render(MyTemplate)
あるいは、トップレベルのscreen
メソッド を使用して、document.body
内の現在レンダリングされているすべてのコンポーネントをクエリすることもできます。
import { render, screen } from "@marko/testing-library"
await render(MyTemplate)
const el = screen.getByText(...)
debug
このメソッドは、container
内のすべての子要素の prettyDOM
をログ出力するためのショートカットです。
import {render} from '@marko/testing-library'
import Greeting from './greeting.marko'
const {debug} = await render(Greeting, {name: 'World'})
debug()
// <h1>Hello World</h1>
// you can also pass an element: debug(getByTestId('messages'))
これは、DOM Testing Library
からも公開されている prettyDOM
をラップしたシンプルなラッパーです。
rerender
Markoコンポーネントのinput
は、親コンポーネントからいつでも変更される可能性があります。多くの場合、この入力は宣言的にコンポーネントを介して渡されますが、コンポーネントが新しいデータに適切に反応することを確認する必要がある場合もあります。rerender
ヘルパーに新しいデータを渡すことで、コンポーネントが新しいinput
を受信することをシミュレートできます。
import {render} from '@marko/testing-library'
import Greeting from './greeting.marko'
const {rerender, debug} = await render(Greeting, {name: 'World'})
// re-render the same component with different props
await rerender({name: 'Marko'})
debug()
// <h1>Hello Marko</h1>
emitted
Markoコンポーネントは、イベントを通して親コンポーネントと通信します。コンポーネントが適切なタイミングで適切なイベントをemitしていることもテストすることをお勧めします。
emitted
ヘルパーはそのように機能します。このヘルパーを呼び出すと、ヘルパーの最後の呼び出し以降にemitされたすべてのイベントが返されます。結果をフィルタリングするためにイベントタイプを渡すこともできます。
import {render, fireEvent} from '@marko/testing-library'
import Counter from './counter.marko'
const {getByText, emitted} = await render(Counter)
const button = getByText('Increment')
await fireEvent.click(button)
await fireEvent.click(button)
// Assuming the `Counter` component forwards these button clicks as `increment` events
expect(emitted('increment')).toHaveProperty('length', 2)
await fireEvent.click(button)
// Note: the tracked events are cleared every time you read them.
// Below we are snapshoting the events after our last assertion,
// the return value will include an array with all of the arguments for each increment event.
expect(emitted('increment')).toMatchInlineSnapshot(`
Array [
Array [
Object {
"count": 3,
},
],
]
`)
// Without an event type will give you all events with their type and arguments.
expect(emitted()).toMatchInlineSnapshot(`
Array [
Object {
"args": Array [
Object {
"count": 0,
},
],
"type": "increment",
},
Object {
"args": Array [
Object {
"count": 1,
},
],
"type": "increment",
},
Object {
"args": Array [
Object {
"count": 3,
},
],
"type": "increment",
}
]
`)
cleanup
トップレベルのcleanupメソッドと同様に、テストが完了する前に、現在レンダリングされているコンポーネントを削除して破棄することができます。
これは、コンポーネントが破棄された後にDOMの変更を適切にクリーンアップすることを検証するのに役立ちます。
import {render, screen, getRoles} from '@marko/testing-library'
import Main from './main.marko'
import Dialog from './dialog.marko'
await render(Main)
const main = screen.getByRole('main')
expect(main).not.toHaveAttribute('aria-hidden')
const {cleanup} = await render(Dialog)
expect(main).toHaveAttribute('aria-hidden') // assert added attribute
cleanup() // destroy the dialog
expect(main).not.toHaveAttribute('aria-hidden') // assert attribute removed
container
レンダリングされたMarkoコンポーネントを含むDOMノード。サーバーサイドテストではJSDOM.fragmentであり、クライアントサイドテストでは、container
レンダリングオプションとして渡されたものになります。
ヒント:レンダリングされた要素のルート要素を取得するには、
container.firstChild
を使用します。
🚨 レンダリングされた要素をクエリするために
container
を使用している場合、再考する必要があります!他のクエリは、テスト対象のコンポーネントに加えられる変更に対してより堅牢になるように設計されています。要素のクエリにはcontainer
を使用しないでください!
fireEvent
MarkoはDOMの更新をバッチ処理して不要な再レンダリングを避けるため、fireEventヘルパーはasync
関数として再エクスポートされます。これにより、テストでトリガーされたイベントに応答してDOMが適切に更新されたことが保証されます。
await fireEvent.click(getByText('Click me'))
cleanup
クライアントサイドテストでは、コンポーネントはプレースホルダーHTMLElementにレンダリングされます。各テストの後でコンポーネントが適切に削除および破棄されるように、サポートされているテストフレームワークでafterEach
にフックすることで、cleanup
メソッドが自動的に呼び出されます。また、いつでも手動でcleanup
を呼び出すことができます。これにより、すべての接続されたコンポーネントが削除されます。
import {render, cleanup, screen} from '@marko/testing-library'
import Greeting from './greeting.marko'
await render(Greeting, {name: 'Marko'})
expect(screen.getByText(/Marko/)).toBeInTheDocument()
// manually cleanup the component before the test is finished
cleanup()
expect(screen.queryByText(/Marko/)).toBeNull()
以下のモジュールをインポートすることで、自動テストクリーンアップをオフにすることができます。
import '@marko/testing-library/dont-cleanup-after-each'
mochaでは、mocha --require @marko/testing-library/dont-cleanup-after-each
をショートカットとして使用できます。
Jestを使用している場合、各ファイルでこれを行うのを避けるために、Jestの設定にsetupFilesAfterEnv: ["@marko/testing-library/dont-cleanup-after-each"]
を含めることができます。