在Jest中模拟document.querySelector

时间:2020-03-21 04:49:58

标签: javascript reactjs dom jestjs mocking

我尝试嘲笑此功能,但没有完整介绍。我不确定如何在getBoundingClientRectheader中嘲笑dataHeader

handleWindowScroll() {

    const header = document.querySelector('.productNavLinksContainer');
    const dataHeader = document.querySelector('.productNavDataContainer');
    if (header && dataHeader) {
        const headerPos = header.getBoundingClientRect();
        const dataHeaderPos = dataHeader.getBoundingClientRect();
        const sticky = header.offsetTop;
        if (dataHeaderPos.top > headerPos.height) {
            this.setState({
                sticky: false
            });
        } else if (window.pageYOffset > sticky) {
            this.setState({
                sticky: true
            });
        }
    } else {
        return;
    }
}

1 个答案:

答案 0 :(得分:1)

这是单元测试解决方案:

index.jsx

import React, { Component } from 'react';

class SomeComponent extends Component {
  constructor(props) {
    super(props);
    this.state = { sticky: false };
  }

  handleWindowScroll() {
    const header = document.querySelector('.productNavLinksContainer');
    const dataHeader = document.querySelector('.productNavDataContainer');
    if (header && dataHeader) {
      const headerPos = header.getBoundingClientRect();
      const dataHeaderPos = dataHeader.getBoundingClientRect();
      const sticky = header.offsetTop;
      if (dataHeaderPos.top > headerPos.height) {
        this.setState({ sticky: false });
      } else if (window.pageYOffset > sticky) {
        this.setState({ sticky: true });
      }
    } else {
      return;
    }
  }
  render() {
    return null;
  }
}

export default SomeComponent;

index.test.jsx

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

describe('60784579', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  it('should not sticky', () => {
    const mDataHeaderPos = { top: 100 };
    const mHeaderPos = { height: 50 };
    const mHeader = { offsetTop: 100, getBoundingClientRect: jest.fn().mockReturnValueOnce(mHeaderPos) };
    const mDataHeader = { getBoundingClientRect: jest.fn().mockReturnValueOnce(mDataHeaderPos) };
    jest.spyOn(document, 'querySelector').mockImplementation((selector) => {
      switch (selector) {
        case '.productNavLinksContainer':
          return mHeader;
        case '.productNavDataContainer':
          return mDataHeader;
      }
    });
    const wrapper = shallow(<SomeComponent></SomeComponent>);
    const instance = wrapper.instance();
    instance.handleWindowScroll();
    expect(document.querySelector).toBeCalledTimes(2);
    expect(mHeader.getBoundingClientRect).toBeCalledTimes(1);
    expect(mDataHeader.getBoundingClientRect).toBeCalledTimes(1);
    expect(instance.state).toEqual({ sticky: false });
  });

  it('should sticky if pageYOffset greater than offsetTop', () => {
    const mDataHeaderPos = { top: 40 };
    const mHeaderPos = { height: 50 };
    const mHeader = { offsetTop: 100, getBoundingClientRect: jest.fn().mockReturnValueOnce(mHeaderPos) };
    const mDataHeader = { getBoundingClientRect: jest.fn().mockReturnValueOnce(mDataHeaderPos) };
    jest.spyOn(document, 'querySelector').mockImplementation((selector) => {
      switch (selector) {
        case '.productNavLinksContainer':
          return mHeader;
        case '.productNavDataContainer':
          return mDataHeader;
      }
    });
    Object.defineProperty(window, 'pageYOffset', { value: 200 });
    const wrapper = shallow(<SomeComponent></SomeComponent>);
    const instance = wrapper.instance();
    instance.handleWindowScroll();
    expect(document.querySelector).toBeCalledTimes(2);
    expect(mHeader.getBoundingClientRect).toBeCalledTimes(1);
    expect(mDataHeader.getBoundingClientRect).toBeCalledTimes(1);
    expect(instance.state).toEqual({ sticky: true });
  });

  it('should do nothing if header or dataHeader does not exist', () => {
    jest.spyOn(document, 'querySelector').mockImplementation((selector) => {
      switch (selector) {
        case '.productNavLinksContainer':
          return undefined;
        case '.productNavDataContainer':
          return undefined;
      }
    });
    const wrapper = shallow(<SomeComponent></SomeComponent>);
    const instance = wrapper.instance();
    const actual = instance.handleWindowScroll();
    expect(actual).toBeUndefined();
    expect(document.querySelector).toBeCalledTimes(2);
    expect(instance.state).toEqual({ sticky: false });
  });
  it('should do nothing if pageYOffset less than offsetTop', () => {
    const mDataHeaderPos = { top: 40 };
    const mHeaderPos = { height: 50 };
    const mHeader = { offsetTop: 100, getBoundingClientRect: jest.fn().mockReturnValueOnce(mHeaderPos) };
    const mDataHeader = { getBoundingClientRect: jest.fn().mockReturnValueOnce(mDataHeaderPos) };
    jest.spyOn(document, 'querySelector').mockImplementation((selector) => {
      switch (selector) {
        case '.productNavLinksContainer':
          return mHeader;
        case '.productNavDataContainer':
          return mDataHeader;
      }
    });
    Object.defineProperty(window, 'pageYOffset', { value: 80 });
    const wrapper = shallow(<SomeComponent></SomeComponent>);
    const instance = wrapper.instance();
    const actual = instance.handleWindowScroll();
    expect(actual).toBeUndefined();
    expect(document.querySelector).toBeCalledTimes(2);
    expect(mHeader.getBoundingClientRect).toBeCalledTimes(1);
    expect(mDataHeader.getBoundingClientRect).toBeCalledTimes(1);
    expect(instance.state).toEqual({ sticky: false });
  });
});

具有100%覆盖率的单元测试结果:

 PASS  stackoverflow/60784579/index.test.jsx (7.912s)
  60784579
    ✓ should not sticky (9ms)
    ✓ should sticky if pageYOffset greater than offsetTop (2ms)
    ✓ should do nothing if header or dataHeader does not exist (2ms)
    ✓ should do nothing if pageYOffset less than offsetTop (1ms)

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

源代码:https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/60784579