我的函数组件对一个元素进行了回调,我想测试这个回调是否在单击时触发。然而,只有当ref的clientHeight和scrollHeight不同时,才会呈现此元素,但在这些测试期间,这两个值都返回为0(当我从useLayoutEffect中登录到控制台时可以看到(。为了进行此测试,我如何将ref.current.clientHeight和ref.current.sollHeight的值设置为不同的非零值?
这是我的组件:
import React, { useState } from "react";
const MyComponent = (props: React.PropsWithChildren<MyComponentProps>) => {
const ref: React.RefObject<HTMLInputElement> = React.CreateRef();
const { myCallBack } = props;
const [showItem, setShowItem] = React.useState(false);
React.useLayoutEffect(() => {
if (ref.current && ref.current.clientHeight < ref.current.ScrollHeight) {
setShowItem(true);
}
}, [ref]);
const someAction = (e: React.ChangeEvent<any>) => {
myCallBack();
}
return(
<div>
<div ref={ref}>
<p>Some text...</p>
</div>
{showItem && <div><p class="some-class" onClick={someAction}>Some more text...</p></div>}
</div>
);
}
export default MyComponent;
这是我的测试:
describe("my tests", (() => {
it("my test", async () => {
let myCallBackMock = jest.fn();
let wrapper = mount(
<MyComponent myCallBack={myCallBackMock} />
);
wrapper.find(".some-class").simulate("click", { type: "click" });
expect(myCallBackMock).toHaveBeenCalled();
});
});
这是我收到的错误消息:
Method "stimulate" is meant to be run on 1 node. 0 found instead.
Jestjs使用jsdom作为其测试环境。JSDOM没有布局引擎。请参阅web平台的未实现部分
Layout:计算CSS将在何处可视化布局元素的能力,这会影响
getBoundingClientRects()
等方法或offsetTop
等属性。
因此,它将为许多与布局相关的属性(如element.clientHeight
(返回零。
在这种情况下,我们必须模拟ref
及其属性。您案例中的clientHeight
和scrollHeight
属性。因此,showItem
状态将在组件装载时设置为true。
尽管我们必须模拟React.createRef
,但我们只是在ref.current
setter函数中为其添加属性和值,并返回它,而不是完全伪造的ref
。这意味着ref.current
仍然引用HTMLdiv元素,而不是伪JS对象。
例如
MyComponent.tsx
:
import React from 'react';
export interface MyComponentProps {
myCallBack(): void;
}
export const MyComponent = ({ myCallBack }: React.PropsWithChildren<MyComponentProps>) => {
const ref: React.RefObject<HTMLInputElement> = React.createRef();
const [showItem, setShowItem] = React.useState(false);
React.useLayoutEffect(() => {
console.log('clientHeight: %s, scrollHeight: %s', ref.current?.clientHeight, ref.current?.scrollHeight);
if (ref.current && ref.current.clientHeight < ref.current.scrollHeight) {
setShowItem(true);
}
}, []);
const someAction = (e: React.ChangeEvent<any>) => {
myCallBack();
};
return (
<div>
<div ref={ref}>
<p>Some text...</p>
</div>
{showItem && (
<div>
<p className="some-class" onClick={someAction}>
Some more text...
</p>
</div>
)}
</div>
);
};
MyComponent.test.tsx
:
import { mount } from 'enzyme';
import React from 'react';
import { MyComponent } from './MyComponent';
export function createFCRefMock(props: { [propName: string]: any }) {
const ref = { current: {} };
const refKey = Symbol('ref');
Object.defineProperty(ref, 'current', {
set(current) {
// intercept the process of setting ref.current
if (current) {
Object.entries(props).forEach(([prop, value]) => {
Object.defineProperty(current, prop, { value });
});
}
this[refKey] = current;
},
get() {
return this[refKey];
},
});
return ref;
}
describe('my tests', () => {
it('my test', async () => {
const myCallBackMock = jest.fn();
const ref = createFCRefMock({ clientHeight: 10, scrollHeight: 20 });
jest.spyOn(React, 'createRef').mockReturnValueOnce(ref);
const wrapper = mount(<MyComponent myCallBack={myCallBackMock} />);
wrapper.find('.some-class').simulate('click');
expect(myCallBackMock).toHaveBeenCalled();
});
});
测试结果:
PASS stackoverflow/73060890/MyComponent.test.tsx (11.738 s)
my tests
✓ my test (73 ms)
console.log
clientHeight: 10, scrollHeight: 20
at stackoverflow/73060890/MyComponent.tsx:11:13
-----------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------------|---------|----------|---------|---------|-------------------
All files | 100 | 78.57 | 100 | 100 |
MyComponent.tsx | 100 | 78.57 | 100 | 100 | 11-12
-----------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 12.354 s