发送自定义事件并测试它是否被正确触发(React TypeScript、Jest)
Dispatch a Custom Event and test if it was correctly triggered (React TypeScript, Jest)
我正在尝试验证位于反应函数 useEffect 挂钩内的自定义事件侦听器,如下所示:
export interface specialEvent extends Event {
detail?: string
}
function Example() {
React.useEffect(()=>{
document.addEventListener('specialEvent', handleChange)
return () => {
document.removeEventListener('specialEvent',handleChange)
}
})
const handleChange = (event:SpecialEvent) => {
...
}
}
我想触发这个自定义事件侦听器并开玩笑地测试它:
it('should trigger "specialEvent" event Listener Properly', async () => {
const specialEvent = new CustomEvent('specialEvent')
const handleChange = jest.fn()
render(<Example />)
await waitFor(() => {
window.document.dispatchEvent(specialEvent)
expect(window.document.dispatchEvent).toHaveBeenNthCalledWith(1, 'specialEvent')
expect(specialEvent).toHaveBeenCalledTimes(1)
})
})
此代码出现以下错误:
expect(received).toHaveBeenNthCalledWith(n, ...expected)
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function dispatchEvent]
按照其中一个答案的建议,我尝试了这个:
//Assert Statements
const specialEvent = new CustomEvent('specialEvent');
const handleSelect = jest.fn();
act(() => {
render(<Example />)
});
await waitFor(() => {
window.document.dispatchEvent(specialEvent)
expect(handleSelect).toHaveBeenCalledTimes(1)
});
但是这次它说预期调用为 1 但收到 0。
谁能帮我解决这个问题?
如错误消息所述,toHaveBeenNthCalledWith
匹配器需要将 mock 或间谍传递给 expect
。
但是,您可能不需要对 window.document.dispatchEvent
被调用做出任何断言,因为您知道您在测试中在上面的行中调用了它。
有关详细信息,请在此处查看 toHaveBeenNthCalledWith
上的文档:https://jestjs.io/docs/expect#tohavebeennthcalledwithnthcall-arg1-arg2-
测试时,引起 React 状态更新的代码应包装到 act(...)
中。如果 handleChange
不会导致 React 状态更新,则不需要使用 act
.
此外,最好不要测试实现细节,对于你的情况,测试实现细节语句是:
expect(window.document.dispatchEvent).toHaveBeenNthCalledWith(1, 'specialEvent')
expect(specialEvent).toHaveBeenCalledTimes(1)
实现细节的每一个小改动都会导致测试用例需要修改。我们应该站在用户的角度来测试UI,用户不关心UI的实现细节,只关心正确渲染UI。
您应该测试的是:当触发自定义事件并且事件处理程序中的状态发生更改时,组件的输出会发生什么情况。
例如
index.tsx
:
import React, { useState } from 'react';
export interface SpecialEvent extends Event {
detail?: string;
}
export function Example() {
const [changed, setChanged] = useState(false);
React.useEffect(() => {
document.addEventListener('specialEvent', handleChange);
return () => {
document.removeEventListener('specialEvent', handleChange);
};
});
const handleChange = (event: SpecialEvent) => {
console.log(event);
setChanged((pre) => !pre);
};
return <div>{changed ? 'a' : 'b'}</div>;
}
index.test.tsx
:
import { render, screen, act } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
import { Example } from './';
describe('70400540', () => {
test('should pass', () => {
const specialEvent = new CustomEvent('specialEvent');
render(<Example />);
expect(screen.getByText('b')).toBeInTheDocument();
act(() => {
window.document.dispatchEvent(specialEvent);
});
expect(screen.getByText('a')).toBeInTheDocument();
});
});
window.document.dispatchEvent(specialEvent)
会导致React状态改变,所以我们把它包装成act(...)
.
测试结果:
PASS examples/70400540/index.test.tsx (11.259 s)
70400540
✓ should pass (59 ms)
console.log
CustomEvent { isTrusted: [Getter] }
at Document.handleChange (examples/70400540/index.tsx:16:13)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 12.655 s
包版本:
"@testing-library/react": "^11.2.2",
"react": "^16.14.0",
"jest": "^26.6.3",
我正在尝试验证位于反应函数 useEffect 挂钩内的自定义事件侦听器,如下所示:
export interface specialEvent extends Event {
detail?: string
}
function Example() {
React.useEffect(()=>{
document.addEventListener('specialEvent', handleChange)
return () => {
document.removeEventListener('specialEvent',handleChange)
}
})
const handleChange = (event:SpecialEvent) => {
...
}
}
我想触发这个自定义事件侦听器并开玩笑地测试它:
it('should trigger "specialEvent" event Listener Properly', async () => {
const specialEvent = new CustomEvent('specialEvent')
const handleChange = jest.fn()
render(<Example />)
await waitFor(() => {
window.document.dispatchEvent(specialEvent)
expect(window.document.dispatchEvent).toHaveBeenNthCalledWith(1, 'specialEvent')
expect(specialEvent).toHaveBeenCalledTimes(1)
})
})
此代码出现以下错误:
expect(received).toHaveBeenNthCalledWith(n, ...expected)
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function dispatchEvent]
按照其中一个答案的建议,我尝试了这个:
//Assert Statements
const specialEvent = new CustomEvent('specialEvent');
const handleSelect = jest.fn();
act(() => {
render(<Example />)
});
await waitFor(() => {
window.document.dispatchEvent(specialEvent)
expect(handleSelect).toHaveBeenCalledTimes(1)
});
但是这次它说预期调用为 1 但收到 0。
谁能帮我解决这个问题?
如错误消息所述,toHaveBeenNthCalledWith
匹配器需要将 mock 或间谍传递给 expect
。
但是,您可能不需要对 window.document.dispatchEvent
被调用做出任何断言,因为您知道您在测试中在上面的行中调用了它。
有关详细信息,请在此处查看 toHaveBeenNthCalledWith
上的文档:https://jestjs.io/docs/expect#tohavebeennthcalledwithnthcall-arg1-arg2-
测试时,引起 React 状态更新的代码应包装到 act(...)
中。如果 handleChange
不会导致 React 状态更新,则不需要使用 act
.
此外,最好不要测试实现细节,对于你的情况,测试实现细节语句是:
expect(window.document.dispatchEvent).toHaveBeenNthCalledWith(1, 'specialEvent')
expect(specialEvent).toHaveBeenCalledTimes(1)
实现细节的每一个小改动都会导致测试用例需要修改。我们应该站在用户的角度来测试UI,用户不关心UI的实现细节,只关心正确渲染UI。
您应该测试的是:当触发自定义事件并且事件处理程序中的状态发生更改时,组件的输出会发生什么情况。
例如
index.tsx
:
import React, { useState } from 'react';
export interface SpecialEvent extends Event {
detail?: string;
}
export function Example() {
const [changed, setChanged] = useState(false);
React.useEffect(() => {
document.addEventListener('specialEvent', handleChange);
return () => {
document.removeEventListener('specialEvent', handleChange);
};
});
const handleChange = (event: SpecialEvent) => {
console.log(event);
setChanged((pre) => !pre);
};
return <div>{changed ? 'a' : 'b'}</div>;
}
index.test.tsx
:
import { render, screen, act } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
import { Example } from './';
describe('70400540', () => {
test('should pass', () => {
const specialEvent = new CustomEvent('specialEvent');
render(<Example />);
expect(screen.getByText('b')).toBeInTheDocument();
act(() => {
window.document.dispatchEvent(specialEvent);
});
expect(screen.getByText('a')).toBeInTheDocument();
});
});
window.document.dispatchEvent(specialEvent)
会导致React状态改变,所以我们把它包装成act(...)
.
测试结果:
PASS examples/70400540/index.test.tsx (11.259 s)
70400540
✓ should pass (59 ms)
console.log
CustomEvent { isTrusted: [Getter] }
at Document.handleChange (examples/70400540/index.tsx:16:13)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 12.655 s
包版本:
"@testing-library/react": "^11.2.2",
"react": "^16.14.0",
"jest": "^26.6.3",