首页 > 解决方案 > 当我使用 document.getElementById (Jest+Enzyme) 时反应测试失败

问题描述

我正在处理一个 React 表单并有一个 onSubmit 函数。我只在onSubmit函数中添加了以下行。

const id = this.getErrorPositionById();
    const errorPosition = document.getElementById(id).offsetTop; //CANNOT READ PROPERTY 'offsetTop' of null
    window.scrollTo({
      top: errorPosition,
      behavior: "smooth"
    });

这就是onSubmit功能。

public getErrorPositionById = () => {
    const error = this.state.errors;
    return Object.keys(error).find(id => error[id] != null);
  };


public onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const id = this.getErrorPositionById();
    const errorPosition = document.getElementById(id).offsetTop; 
    window.scrollTo({
      top: errorPosition,
      behavior: "smooth"
    });
    if (
      !this.state.isValid ||
      (!this.props.allowMultipleSubmits && this.state.isSubmitted)
    ) {
      return;
    }
    Promise.all(this.validateFields())
      .then((validationErrors: IValidationErrors[]) => {
        this.setError(Object.assign({}, ...validationErrors), () => {
          this.isFormValid() ? this.setSubmitted(e) : this.scrollFormToView();
        });
      })
      .then(() => {
        const newErrors = this.state.errors;
        this.setState({ errors: { ...newErrors, ...this.props.apiErrors } });
      });
  };

这是测试用例

  beforeEach(() => {
    jest.clearAllMocks();
    formFields = jest.fn();
    onSubmit = jest.fn();
    onValidate = jest.fn();
    validate = jest.fn();
    mockPreventDefault = jest.fn();
    mockEvent = jest.fn(() => ({ preventDefault: mockPreventDefault }));
    mockValidateAllFields = jest.fn(() => Promise);
    mockChildFieldComponent = { validate };
    instance = formWrapper.instance();
  });


it("should not reValidate if form has been submitted already", () => {
    instance.validateFields = mockValidateAllFields;
    instance.setSubmitted();
    expect(instance.state.isSubmitted).toBe(true);
    instance.onSubmit(mockEvent());
    expect(mockValidateAllFields).toHaveBeenCalledTimes(0);
  });

测试用例失败并出现错误

TypeError:无法读取 null 的属性“offsetTop”

在下一行

const errorPosition = document.getElementById(id).offsetTop; 

有人可以帮助我了解如何消除此错误。

标签: javascriptreactjsreact-nativejestjsenzyme

解决方案


你应该为document.getElementById(id). 为简单起见,我从组件中删除了您的业务逻辑。

例如

index.tsx

import React, { Component } from 'react';

class SomeComponent extends Component {
  state = {
    errors: {
      '#selector': {},
    },
  };

  public getErrorPositionById = () => {
    const error = this.state.errors;
    return Object.keys(error).find((id) => error[id] != null);
  };

  public onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const id = this.getErrorPositionById() as string;
    const errorPosition = document.getElementById(id)!.offsetTop;
    window.scrollTo({
      top: errorPosition,
      behavior: 'smooth',
    });
  };

  public render() {
    return (
      <div>
        <form onSubmit={this.onSubmit}></form>
      </div>
    );
  }
}

export default SomeComponent;

index.spec.tsx

import React from 'react';
import { shallow } from 'enzyme';
import SomeComponent from './';

describe('SomeComponent', () => {
  afterEach(() => {
    jest.resetAllMocks();
  });
  it('should handle submit correctly', async () => {
    const mElement = { offsetTop: 123 };
    document.getElementById = jest.fn().mockReturnValueOnce(mElement);
    window.scrollTo = jest.fn();
    const wrapper = shallow(<SomeComponent></SomeComponent>);
    const mEvent = { preventDefault: jest.fn() };
    wrapper.find('form').simulate('submit', mEvent);
    expect(mEvent.preventDefault).toHaveBeenCalledTimes(1);
    expect(document.getElementById).toBeCalledWith('#selector');
    expect(window.scrollTo).toBeCalledWith({ top: 123, behavior: 'smooth' });
  });
});

带有覆盖率报告的单元测试结果:

 PASS  src/stackoverflow/53352420/index.spec.tsx (12.859s)
  SomeComponent
    ✓ should handle submit correctly (20ms)

-----------|----------|----------|----------|----------|-------------------|
File       |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files  |      100 |      100 |      100 |      100 |                   |
 index.tsx |      100 |      100 |      100 |      100 |                   |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        14.751s

源代码:https ://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/53352420


推荐阅读