首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >有lodash.debounce延迟故障的测试组件

有lodash.debounce延迟故障的测试组件
EN

Stack Overflow用户
提问于 2021-01-03 19:36:10
回答 1查看 2.5K关注 0票数 5

我有一个丰富的文本编辑器输入字段,我想用一个已弹出的组件包装。已取消的输入组件如下所示:

代码语言:javascript
复制
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的包装组件。

代码语言:javascript
复制
<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标记:

代码语言:javascript
复制
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;

我就是这样测试这个的:

代码语言:javascript
复制
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);
});

当我运行这个测试时,我得到:

代码语言:javascript
复制
Error: expect(received).toEqual(expected) // deep equality

Expected: "<p><b>New text, Flesk</b></p>"
Received: "Lorem ipsum ...?"

在检查结果之前,我还尝试过用jest.runAllTimers();运行测试,但之后我得到:

代码语言:javascript
复制
Error: Ran 100000 timers, and there are still more! Assuming we've hit an infinite recursion and bailing out...

我也曾尝试过:

代码语言:javascript
复制
jest.advanceTimersByTime(500);

但是考试总是失败,我得到了旧的文本状态。状态似乎不会因为某种原因而改变,这是很奇怪的,因为在我用DebounceInput组件包装组件之前,组件和测试都是绿色的。具有MediumEditor的父组件有一个应该从DebounceInput组件调用的方法onTextChange,因为它是作为onChange支柱传递给DebounceInput,的函数,但是在测试中,我可以看到这个方法是永远无法实现的。在浏览器中,一切都很好,所以我不知道它为什么不能在测试中工作?

代码语言:javascript
复制
onTextChange(text) {
  console.log('text', text);
  this.setState((state) => {
    return {
      task: { ...state.task, text },
      isDirty: true,
    };
  });
}

在进一步的检查中,我可以看到在测试中通过了正确的值,一直到handleChange in DebouncedInput.。因此,我怀疑,在这个测试中,lodash.debounce存在一些问题。我不确定我是应该嘲笑这个函数,还是嘲笑它?

代码语言:javascript
复制
const handleChange = (nextValue) => {
  console.log(nextValue);
  setValue(nextValue);
  debouncedSave(nextValue);
};

这就是我怀疑测试中的问题所在:

代码语言:javascript
复制
const useDebounce = (callback, delay) => {
  const debouncedFn = useCallback(
    debounce((...args) => callback(...args), delay),
    [delay] // will recreate if delay changes
  );
  return debouncedFn;
};

我试过像这样嘲弄别人:

代码语言:javascript
复制
import debounce from 'lodash.debounce'
jest.mock('lodash.debounce');
debounce.mockImplementation(() => jest.fn(fn => fn));

这让我犯了错误:

TypeError:_lodash.default.mockImplementation不是一个函数

我该怎么解决这个问题?

EN

回答 1

Stack Overflow用户

发布于 2021-01-13 19:26:04

我猜你正在使用酶(从道具访问)。为了测试一些依赖于jest定时器的代码

请注意,当您从酶中更改状态时,需要使用jest.useFakeTimers()

  • advance,使用jest.runOnlyPendingTimers()

调用计时器。

这应该能行。

很少有关于测试反应组件的附带说明:

如果您想测试functionality

  • Don't的功能,测试即时组件(在您的例子中是MediumEditor),测试整个包装组件以测试onChange

  • 从测试中更新状态是没有意义的,它使您的测试高度耦合到特定的实现,证明,重命名状态变量名,组件的功能不会改变,但是测试会失败,由于它们将尝试更新无现有状态的状态,所以variable.

  • Don't从测试中调用onChange道具(或任何其他道具)。它使您的测试更加了解实现(=与组件实现高度耦合),实际上它们没有检查您的组件是否正常工作,例如,考虑到由于某些原因您没有将onChange支柱传递给输入,您的测试就会通过(因为您的测试正在调用onChange支柱),但实际上它无法工作。

组件测试的最佳方法是像用户将要做的那样模拟组件上的操作,例如,在输入组件中,模拟组件上的更改/输入事件(这是用户在实际应用程序中输入时所做的)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65554104

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档