【发布时间】:2021-04-09 17:45:38
【问题描述】:
我有一个富文本编辑器输入字段,我想用去抖动组件包装它。去抖输入组件如下所示:
import { useState, useCallback } from 'react';
import debounce from 'lodash.debounce';
const useDebounce = (callback, delay) => {
const debouncedFn = useCallback(
debounce((...args) => callback(...args), delay),
[delay] // will recreate if delay changes
);
return debouncedFn;
};
function DebouncedInput(props) {
const [value, setValue] = useState(props.value);
const debouncedSave = useDebounce((nextValue) => props.onChange(nextValue), props.delay);
const handleChange = (nextValue) => {
setValue(nextValue);
debouncedSave(nextValue);
};
return props.renderProps({ onChange: handleChange, value });
}
export default DebouncedInput;
我使用 DebouncedInput 作为 MediumEditor 的包装组件:
<DebouncedInput
value={task.text}
onChange={(text) => onTextChange(text)}
delay={500}
renderProps={(props) => (
<MediumEditor
{...props}
id="task"
style={{ height: '100%' }}
placeholder="Task text…"
disabled={readOnly}
key={task.id}
/>
)}
/>;
MediumEditor 组件做了一些我想测试的卫生工作,例如剥离 html 标签:
class MediumEditor extends React.Component {
static props = {
id: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func,
disabled: PropTypes.bool,
uniqueID: PropTypes.any,
placeholder: PropTypes.string,
style: PropTypes.object,
};
onChange(text) {
this.props.onChange(stripHtml(text) === '' ? '' : fixExcelPaste(text));
}
render() {
const {
id,
value,
onChange,
disabled,
placeholder,
style,
uniqueID,
...restProps
} = this.props;
return (
<div style={{ position: 'relative', height: '100%' }} {...restProps}>
{disabled && (
<div
style={{
position: 'absolute',
width: '100%',
height: '100%',
cursor: 'not-allowed',
zIndex: 1,
}}
/>
)}
<Editor
id={id}
data-testid="medium-editor"
options={{
toolbar: {
buttons: ['bold', 'italic', 'underline', 'subscript', 'superscript'],
},
spellcheck: false,
disableEditing: disabled,
placeholder: { text: placeholder || 'Skriv inn tekst...' },
}}
onChange={(text) => this.onChange(text)}
text={value}
style={{
...style,
background: disabled ? 'transparent' : 'white',
borderColor: disabled ? 'grey' : '#FF9600',
overflowY: 'auto',
color: '#444F55',
}}
/>
</div>
);
}
}
export default MediumEditor;
这就是我测试这个的方式:
it('not stripping html tags if there is text', async () => {
expect(editor.instance.state.text).toEqual('Lorem ipsum ...?');
const mediumEditor = editor.findByProps({ 'data-testid': 'medium-editor' });
const newText = '<p><b>New text, Flesk</b></p>';
mediumEditor.props.onChange(newText);
// jest.runAllTimers();
expect(editor.instance.state.text).toEqual(newText);
});
当我运行这个测试时,我得到:
Error: expect(received).toEqual(expected) // deep equality
Expected: "<p><b>New text, Flesk</b></p>"
Received: "Lorem ipsum ...?"
在检查结果之前,我也尝试使用 jest.runAllTimers(); 运行测试,但随后我得到:
Error: Ran 100000 timers, and there are still more! Assuming we've hit an infinite recursion and bailing out...
我也尝试过:
jest.advanceTimersByTime(500);
但是测试一直失败,我得到了文本的旧状态。
似乎状态由于某种原因没有改变,这很奇怪,因为在我用 DebounceInput 组件包装它们之前,组件曾经工作并且测试是绿色的。
我拥有 MediumEditor 的父组件有一个方法 onTextChange 应该从 DebounceInput 组件调用,因为这是作为传递的函数DebounceInput 的onChange 属性,但在测试中,我可以看到这种方法永远无法实现。在浏览器中,一切正常,所以我不知道为什么它在测试中不起作用?
onTextChange(text) {
console.log('text', text);
this.setState((state) => {
return {
task: { ...state.task, text },
isDirty: true,
};
});
}
在进一步检查时,我可以看到正确的值在测试中一直传递到 DebouncedInput 中的 handleChange。所以,我怀疑在这个测试中 lodash.debounce 存在一些问题。我不确定我是否应该模拟这个函数或者模拟是否带有笑话?
const handleChange = (nextValue) => {
console.log(nextValue);
setValue(nextValue);
debouncedSave(nextValue);
};
这是我怀疑问题出在测试的地方:
const useDebounce = (callback, delay) => {
const debouncedFn = useCallback(
debounce((...args) => callback(...args), delay),
[delay] // will recreate if delay changes
);
return debouncedFn;
};
我尝试过像这样模拟去抖动:
import debounce from 'lodash.debounce'
jest.mock('lodash.debounce');
debounce.mockImplementation(() => jest.fn(fn => fn));
这给了我错误:
TypeError: _lodash.default.mockImplementation is not a function
我应该如何解决这个问题?
【问题讨论】:
-
试试 jest.advanceTimersByTime(n),n 等于 500,因为 delay={500}
-
我试过了,但测试一直失败,状态似乎因为某种原因没有改变,这很奇怪,因为在我之前该组件可以工作并且测试是绿色的它们用 DebounceInput 组件包裹。
-
你用什么库来测试?
-
仅反应测试渲染
-
如果您mock
lodash/debounce,您的测试是否通过?import debouce from 'lodash/debounce'; // Tell jest to mock this import jest.mock('lodash/debounce'); // Assign the import a new implementation, in this case it's execute the function given to you debouce.mockImplementation(fn => fn);
标签: reactjs jestjs lodash debounce