前回作成したコンポーネントのテストコードを書いてみた所、ResizeObserver と Element.getBoundingClientRect をモックにする必要がありました。
どの様にしたかを記述します。
モック化したテストコード
まず、実際にモックにしたテストコードは以下の通りです。
import React from 'react';
import { act } from 'react-dom/test-utils';
import { render, screen } from '@testing-library/react';
import { Text } from '../Text';
let instanceResize: ResizeObserver | null = null;
let callbackResize: ResizeObserverCallback | null = null;
global.ResizeObserver = class mockResizeObjerver {
constructor(callback: ResizeObserverCallback) {
instanceResize = this;
callbackResize = callback;
}
disconnect(){ }
observe(target: Element, options?: ResizeObserverOptions){ }
unobserve(target: Element){ }
}
describe('Textのテスト', () => {
test('省略表示されること', () => {
jest.spyOn(Element.prototype, 'getBoundingClientRect')
// rectOuter
.mockImplementationOnce(jest.fn(() => ({x:0, y:10, width: 50, height: 50}) as DOMRect))
// rectInner
.mockImplementationOnce(jest.fn(() => ({x:0, y:10, width: 100, height: 50}) as DOMRect));
const result = render(<Text>abcdefghijklmnopqrstuvwxyz</Text>);
act(() => {
callbackResize!([], instanceResize!);
});
// console.log('省略表示されること', screen.debug());
const outer = result.container.querySelectorAll('span[title]');
expect(outer.length).toBe(1);
});
test('省略表示されないこと', () => {
const result = render(<Text>abcdefghijklmnopqrstuvwxyz</Text>);
act(() => {
callbackResize!([], instanceResize!);
});
// console.log('省略表示されないこと', screen.debug());
const outer = result.container.querySelectorAll('span[title]');
expect(outer.length).toBe(0);
});
});
ResizeObserver をモックしないとどうなるか
以下の通り、「ResizeObserver は未定義」でコケます。
● Textのテスト › 省略表示されること
ReferenceError: ResizeObserver is not defined
13 | // リサイズ監視
14 | useEffect(() => {
> 15 | const resizeObserver = new ResizeObserver(() => {
| ^
16 | const rectOuter = refOuter.current?.getBoundingClientRect();
17 | const rectInner = refInner.current?.getBoundingClientRect();
18 | // 外枠 < 内枠 なら省略表記
ということで、以下の様にモックにします。
let instanceResize: ResizeObserver | null = null;
let callbackResize: ResizeObserverCallback | null = null;
global.ResizeObserver = class mockResizeObjerver {
constructor(callback: ResizeObserverCallback) {
instanceResize = this;
callbackResize = callback;
}
disconnect(){ }
observe(target: Element, options?: ResizeObserverOptions){ }
unobserve(target: Element){ }
}
コンストラクタに渡すコールバック関数が呼ばれないとリサイズの処理がされないので、callbackResize を外出ししてテストコード中に呼んでいます。
コールバックに渡す引数に ResizeObserver のインスタンスが必要なので、こちらも instanceResize に外出ししています。
Element.getBoundingClientRect をモックしないとどうなるか
「省略表示されること」のテストでコケます。
● Textのテスト › 省略表示されること
expect(received).toBe(expected) // Object.is equality
Expected: 1
Received: 0
20 | const result = render(<Text>abcdefghijklmnopqrstuvwxyz</Text>);
21 | const outer = result.container.querySelectorAll('span[title]');
> 22 | expect(outer.length).toBe(1);
| ^
23 | });
24 | test('省略表示されないこと', () => {
25 | const result = render(<Text>abcdefghijklmnopqrstuvwxyz</Text>);
描画内容を確認する為、screen.debug() を console.log してみると、
<body>
<div>
<span
class="text"
>
<span>
abcdefghijklmnopqrstuvwxyz
</span>
</span>
</div>
</body>
の通り、title 属性がありません。確認してみると、getBoundingClientRect が返してくる値が、
{ bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0 }
と、全部ゼロ。試しに幅を指定した要素の子要素にText コンポーネントを入れても変わりませんでした。ということで、
jest.spyOn(Element.prototype, 'getBoundingClientRect')
// rectOuter
.mockImplementationOnce(jest.fn(() => ({x:0, y:10, width: 50, height: 50}) as DOMRect))
// rectInner
.mockImplementationOnce(jest.fn(() => ({x:0, y:10, width: 100, height: 50}) as DOMRect));
のように、呼び出された1回目は幅50、2回目は幅100、を返すモックにして省略表示が必要となる状況にしました。
テストしている内容
省略表示した場合は、
<body>
<div>
<span
class="text"
>
<span
title="abcdefghijklmnopqrstuvwxyz"
>
abcdefghijklmnopqrstuvwxyz
</span>
</span>
</div>
</body>
省略表示しない場合は、
<body>
<div>
<span
class="text"
>
<span>
abcdefghijklmnopqrstuvwxyz
</span>
</span>
</div>
</body>
という描画結果なので、title 属性がある span の有無をテストしています。
コメント