Lef*_*eff 5 debouncing lodash reactjs jestjs
我有一个富文本编辑器输入字段,我想用去抖组件包裹它。去抖输入组件如下所示:
\nimport { useState, useCallback } from \'react\';\nimport debounce from \'lodash.debounce\';\n\nconst useDebounce = (callback, delay) => {\n const debouncedFn = useCallback(\n debounce((...args) => callback(...args), delay),\n [delay] // will recreate if delay changes\n );\n return debouncedFn;\n};\n\nfunction DebouncedInput(props) {\n const [value, setValue] = useState(props.value);\n const debouncedSave = useDebounce((nextValue) => props.onChange(nextValue), props.delay);\n\n const handleChange = (nextValue) => {\n setValue(nextValue);\n debouncedSave(nextValue);\n };\n\n return props.renderProps({ onChange: handleChange, value });\n}\n\nexport default DebouncedInput;\nRun Code Online (Sandbox Code Playgroud)\n我使用DebouncedInput作为MediumEditor的包装组件:
\n<DebouncedInput\n value={task.text}\n onChange={(text) => onTextChange(text)}\n delay={500}\n renderProps={(props) => (\n <MediumEditor\n {...props}\n id="task"\n style={{ height: \'100%\' }}\n placeholder="Task text\xe2\x80\xa6"\n disabled={readOnly}\n key={task.id}\n />\n )}\n/>;\nRun Code Online (Sandbox Code Playgroud)\nMediumEditor组件做了一些我想测试的清理工作,例如剥离 html 标签:
\nclass MediumEditor extends React.Component {\n static props = {\n id: PropTypes.string,\n value: PropTypes.string,\n onChange: PropTypes.func,\n disabled: PropTypes.bool,\n uniqueID: PropTypes.any,\n placeholder: PropTypes.string,\n style: PropTypes.object,\n };\n\n onChange(text) {\n this.props.onChange(stripHtml(text) === \'\' ? \'\' : fixExcelPaste(text));\n }\n\n render() {\n const {\n id,\n value,\n onChange,\n disabled,\n placeholder,\n style,\n uniqueID,\n ...restProps\n } = this.props;\n return (\n <div style={{ position: \'relative\', height: \'100%\' }} {...restProps}>\n {disabled && (\n <div\n style={{\n position: \'absolute\',\n width: \'100%\',\n height: \'100%\',\n cursor: \'not-allowed\',\n zIndex: 1,\n }}\n />\n )}\n <Editor\n id={id}\n data-testid="medium-editor"\n options={{\n toolbar: {\n buttons: [\'bold\', \'italic\', \'underline\', \'subscript\', \'superscript\'],\n },\n spellcheck: false,\n disableEditing: disabled,\n placeholder: { text: placeholder || \'Skriv inn tekst...\' },\n }}\n onChange={(text) => this.onChange(text)}\n text={value}\n style={{\n ...style,\n background: disabled ? \'transparent\' : \'white\',\n borderColor: disabled ? \'grey\' : \'#FF9600\',\n overflowY: \'auto\',\n color: \'#444F55\',\n }}\n />\n </div>\n );\n }\n}\n\nexport default MediumEditor;\nRun Code Online (Sandbox Code Playgroud)\n这就是我测试的方式:
\nit(\'not stripping html tags if there is text\', async () => {\n expect(editor.instance.state.text).toEqual(\'Lorem ipsum ...?\');\n const mediumEditor = editor.findByProps({ \'data-testid\': \'medium-editor\' });\n const newText = \'<p><b>New text, Flesk</b></p>\';\n mediumEditor.props.onChange(newText);\n // jest.runAllTimers();\n expect(editor.instance.state.text).toEqual(newText);\n});\nRun Code Online (Sandbox Code Playgroud)\n当我运行这个测试时,我得到:
\nError: expect(received).toEqual(expected) // deep equality\n\nExpected: "<p><b>New text, Flesk</b></p>"\nReceived: "Lorem ipsum ...?"\nRun Code Online (Sandbox Code Playgroud)\njest.runAllTimers();在检查结果之前我也尝试过运行测试,但后来我得到:
Error: Ran 100000 timers, and there are still more! Assuming we\'ve hit an infinite recursion and bailing out...\nRun Code Online (Sandbox Code Playgroud)\n我也尝试过:
\nError: expect(received).toEqual(expected) // deep equality\n\nExpected: "<p><b>New text, Flesk</b></p>"\nReceived: "Lorem ipsum ...?"\nRun Code Online (Sandbox Code Playgroud)\n但测试一直失败,我得到了文本的旧状态。\n似乎状态由于某种原因没有改变,这很奇怪,因为组件曾经工作过,并且在我包装它们之前测试是绿色的\n我拥有MediumEditor的父组件有一个方法onTextChange ,应该从DebounceInput组件调用它,因为这是作为onChangeprops 传递给DebounceInput 的函数,但在测试中,我可以看到这个方法永远不会达到。在浏览器中一切正常,所以我不知道为什么它在测试中不起作用?
Error: Ran 100000 timers, and there are still more! Assuming we\'ve hit an infinite recursion and bailing out...\nRun Code Online (Sandbox Code Playgroud)\n在进一步检查时,我可以看到正确的值在测试中一直传递到DebouncedInput中的handleChange。所以,我怀疑,在这个测试中, lodash.debounce存在一些问题。我不确定我是否应该模拟这个函数,或者模拟是否带有玩笑?
\njest.advanceTimersByTime(500);\nRun Code Online (Sandbox Code Playgroud)\n这是我怀疑测试中问题所在的地方:
\nonTextChange(text) {\n console.log(\'text\', text);\n this.setState((state) => {\n return {\n task: { ...state.task, text },\n isDirty: true,\n };\n });\n}\nRun Code Online (Sandbox Code Playgroud)\n我尝试过像这样嘲笑反跳:
\nconst handleChange = (nextValue) => {\n console.log(nextValue);\n setValue(nextValue);\n debouncedSave(nextValue);\n};\nRun Code Online (Sandbox Code Playgroud)\n这给了我错误:
\n\n\nRun Code Online (Sandbox Code Playgroud)\nTypeError: _lodash.default.mockImplementation is not a function\n
我应该如何解决这个问题?
\n我猜你正在使用酶(来自道具访问)。为了测试一些依赖于计时器的代码jest:
jest.useFakeTimers()componentWrapper.update()jest.runOnlyPendingTimers()这应该有效。
关于测试 React 组件的一些附注:
onChange,测试直接组件(在您的情况下MediumEditor),则没有必要测试整个包装组件来测试 onChange 功能onChange从测试中调用 props (或任何其他 props)。它使您的测试更加了解实现(=与组件实现高度耦合),实际上它们不会检查您的组件是否正常工作,例如,考虑到由于某种原因您没有将 prop 传递onChange给输入,您的测试将通过(因为您的测试正在调用该onChange道具),但实际上它不起作用。组件测试的最佳方法是像用户一样模拟组件上的操作,例如,在输入组件中,模拟组件上的更改/输入事件(这是用户在实际应用程序中键入时所做的操作)。
| 归档时间: |
|
| 查看次数: |
3605 次 |
| 最近记录: |