如何在Jest中模拟IntersectionObserver API?

时间:2020-10-01 14:17:25

标签: javascript unit-testing jestjs

我已经阅读了有关此主题的所有相关问题,并且我意识到这可能会被标记为重复项,但是我一生都无法弄清楚如何使它正常工作。

我有一个简单的函数,可以延迟加载元素:

export default function lazyLoad(targets, onIntersection) {
  const observer = new IntersectionObserver((entries, self) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        onIntersection(entry.target);
        self.unobserve(entry.target);
      }
    });
  });

  document.querySelectorAll(targets).forEach((target) => observer.observe(target));
  return observer;
}

用法示例:

lazyLoad('.lazy-img', (img) => {
  const pictureElement = img.parentElement;
  const source = pictureElement.querySelector('.lazy-source');

  source.srcset = source.getAttribute('data-srcset');
  img.src = img.getAttribute('data-src');
});

现在,我正在尝试使用玩笑来测试lazyLoad函数,但是由于它是浏览器API,而不是本机JavaScript,因此我显然需要模拟IntersectionObserver。

以下用于测试observe方法的工作:

let observe;
let unobserve;

beforeEach(() => {
  observe = jest.fn();
  unobserve = jest.fn();

  window.IntersectionObserver = jest.fn(() => ({
    observe,
    unobserve,
  }));
});

describe('lazyLoad utility', () => {
  it('calls observe on each target', () => {
    for (let i = 0; i < 3; i++) {
      const target = document.createElement('img');
      target.className = 'lazy-img';
      document.body.appendChild(target);
    }

    lazyLoad(
      '.lazy-img',
      jest.fn(() => {})
    );

    expect(observe).toHaveBeenCalledTimes(3);
  });
});

但是我也想测试.isIntersecting逻辑,在该逻辑上触发回调……除非我不知道该怎么做。如何测试带有笑话的路口?

2 个答案:

答案 0 :(得分:2)

当您将其作为参数传递时,模拟的东西非常简单:

export default function lazyLoad(targets, onIntersection, observerClass = IntersectionObserver) {
  const observer = new observerClass(...)
  ...
}


// test file
let entries = [];
const observeFn = jest.fn();
const unobserveFn = jest.fn()
class MockObserver {
  constructor(fn) {
    fn(entries,this);
  }

  observe() { observeFn() }
  unobserve() { unobserveFn() }
}

test('...',() => {
   // set `entries` to be something so you can mock it
   entries = ...something
   lazyLoad('something',jest.fn(),MockObserver);
});

答案 1 :(得分:1)

另一种选择是使用上面提到的 mockObserver 并在窗口中模拟。

window.IntersetionObserver = mockObserver

而且你不需要在组件 props 中传递观察者。

测试条目是否为Intesecting的重点是将IntersectionObserver模拟为如上所述的类。