【问题标题】:How do I mock the IntersectionObserver API in Jest?如何在 Jest 中模拟 IntersectionObserver API?
【发布时间】:2020-10-01 14:17:25
【问题描述】:

我已阅读有关此主题的所有相关问题,并且我意识到这可能会被标记为重复,但我终其一生都无法弄清楚如何让它发挥作用。

我有一个懒加载元素的简单函数:

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');
});

现在,我正在尝试使用 jest 测试 lazyLoad 函数,但我显然需要模拟 IntersectionObserver,因为它是浏览器 API,而不是原生 JavaScript。

以下工作用于测试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 逻辑......但我不知道该怎么做。如何用 jest 测试交叉点?

【问题讨论】:

  • 将其作为参数传递。
  • @Adam 你能澄清一下你的意思吗?我应该通过什么作为论点?谢谢!
  • lazyLoad(targets,onIntersection,observer = IntersectionObserver)
  • @Adam 谢谢!不幸的是,我已经阅读了那个 SO 帖子,但它对我没有帮助。我主要停留在如何模拟 .isIntersecting 检查 IntersectionObserver。

标签: javascript unit-testing jestjs


【解决方案1】:

当你将它作为参数传递时,模拟东西非常容易:

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);
});

【讨论】:

  • 有道理,谢谢!我没想到要像这样使用依赖注入。不过,我想我需要@babel/plugin-proposal-class-properties,对吧?因为我做不到observe = jest.fn()
  • @AleksandrH 我改变了语法,依赖注入是主要思想,其余的间谍只做你需要做的事情。
  • @AleksandrH 让这成为如何编写可测试代码的杰出时刻之一 - 无论您需要模拟什么,只需将其作为参数即可。
  • 这对我来说无疑是一个光明的时刻,谢谢!我现在为这个实用程序编写测试要容易得多。
【解决方案2】:

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

window.IntersetionObserver = mockObserver

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

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

【讨论】:

    猜你喜欢
    • 2019-11-29
    • 2020-03-12
    • 1970-01-01
    • 2020-10-02
    • 2021-06-09
    • 1970-01-01
    • 2019-01-23
    • 2021-07-03
    • 2020-07-25
    相关资源
    最近更新 更多