【问题标题】:Testing MutationObserver with Jest用 Jest 测试 MutationObserver
【发布时间】:2018-07-26 08:35:48
【问题描述】:

我编写了一个脚本,主要目的是为某些表格的单元格添加新元素。

测试是这样完成的:

document.body.innerHTML = `
<body>
    <div id="${containerID}">
        <table>
            <tr id="meta-1"><td> </td></tr>
            <tr id="meta-2"><td> </td></tr>
            <tr id="meta-3"><td> </td></tr>
            <tr id="no-meta-1"><td> </td></tr>
        </table>
    </div>
</body>
`;

    const element = document.querySelector(`#${containerID}`);

    const subject = new WPMLCFInfoHelper(containerID);
    subject.addInfo();

    expect(mockWPMLCFInfoInit).toHaveBeenCalledTimes(3);

mockWPMLCFInfoInit 在调用时告诉我元素已添加到单元格中。

当向表中添加新行时,部分代码使用 MutationObserver 再次调用 mockWPMLCFInfoInit

new MutationObserver((mutations) => {
    mutations.map((mutation) => {
        mutation.addedNodes && Array.from(mutation.addedNodes).filter((node) => {
            console.log('New row added');
            return node.tagName.toLowerCase() === 'tr';
        }).map((element) => WPMLCFInfoHelper.addInfo(element))
    });
}).observe(metasTable, {
    subtree:   true,
    childList: true
});

WPMLCFInfoHelper.addInfomockWPMLCFInfoInit 的真实版本(当然,这是一个模拟方法)。

从上面的测试中,如果添加类似的东西......

const table = element.querySelector(`table`);
var row = table.insertRow(0);

console.log('New row added'); 永远不会被调用。 可以肯定的是,我也尝试在新行中添加所需的单元格。

当然,手动测试告诉我代码有效。

搜了一圈,我的理解是不支持MutationObserver和there is no plan to support it

很公平,但在这种情况下,我该如何测试我的这部分代码?除了手动,那就是:)

【问题讨论】:

  • 您需要创建一个 MutationObserver 的模拟实现,它的行为符合您的需要。

标签: jestjs


【解决方案1】:

我知道我在这里聚会迟到了,但在我的笑话设置文件中,我只是添加了以下模拟 MutationObserver 类。

global.MutationObserver = class {
    constructor(callback) {}
    disconnect() {}
    observe(element, initObject) {}
};

这显然不允许您测试观察者是否按照您的意愿行事,但会允许您的代码的其余测试运行,这是通向有效解决方案的途径。

【讨论】:

  • 请记住,这不会测试突变观察者的东西。我还没有找到一个好的解决方案,所以我的代码中变异观察者的部分仍然很可怕。
  • 您可以使用 Jest 模拟函数来模拟 MutationObserver。这允许您在代码中测试它的实例。有关示例,请参见 my answer
  • 出于某种原因,这对我不起作用。必须在测试文件中定义,真的很痛苦。
【解决方案2】:

这个问题实际上是因为JSDom不支持MutationObserver而出现的,所以你必须提供一个合适的polyfill

有点棘手的想法可能不是最好的解决方案(让我们使用旨在与 IE9-10 兼容的库)。

第 1 步(将此库安装到 devDependencies)

npm install --save-dev mutation-observer

第 2 步(导入和全局化)

import MutationObserver from 'mutation-observer'
global.MutationObserver = MutationObserver 

test('your test case', () => { 
   ...
})

【讨论】:

    【解决方案3】:

    您可以使用mutationobserver-shim

    在 setup.js 中添加这个

    import "mutationobserver-shim"
    

    并安装

    npm i -D mutationobserver-shim
    

    【讨论】:

      【解决方案4】:

      我认为解决方案的很大一部分只是思维方式的转变。单元测试不应该确定MutationObserver 是否正常工作。假设它是,并模拟您的代码利用的部分。

      只需提取您的回调函数,以便对其进行独立测试;然后,模拟MutationObserver(如samuraiseoul's answer)以防止错误。将模拟的 MutationRecord 列表传递给您的回调并测试结果是否符合预期。

      也就是说,使用 Jest 模拟函数来模拟 MutationObserver 及其 observe()disconnect() 方法至少可以让您检查已创建的 MutationObserver 实例的数量以及这些方法是否已被调用在预期的时间。

      const mutationObserverMock = jest.fn(function MutationObserver(callback) {
          this.observe = jest.fn();
          this.disconnect = jest.fn();
          // Optionally add a trigger() method to manually trigger a change
          this.trigger = (mockedMutationsList) => {
              callback(mockedMutationsList, this);
          };
      });
      global.MutationObserver = mutationObserverMock;
      
      it('your test case', () => {
          // after new MutationObserver() is called in your code
          expect(mutationObserverMock.mock.instances).toBe(1);
      
          const [observerInstance] = mutationObserverMock.mock.instances;
          expect(observerInstance.observe).toHaveBeenCalledTimes(1);
      });
      
      

      【讨论】:

      • 这看起来正是我想要的!除了在我的设置中它没有任何效果。我收到此错误“[BootstrapVue 警告]:observeDom:需要 MutationObserver 支持。”使用 jest、bootstrapVue、vue-test-utils 进行 vue 单元测试。任何进一步的提示都会很棒。
      • @rodhoward 虽然我无法谈论 BootstrapVue 可能会发生什么,但我的示例是在使用 vue-test-utils 的 vue-cli 项目中使用的。不过,我们在自己的代码中手动使用了 MutationObserver。 BootstrapVue 有一个hasMutationObserverSupport 签入src/utils/env.js。我最好的猜测是它与它在node_modules 中有关,以及 jest 是如何处理的?
      【解决方案5】:

      由于这里没有提到:jsdom 已经支持 MutationObserver 有一段时间了。

      这是实现它的 PR https://github.com/jsdom/jsdom/pull/2398

      【讨论】:

      • 为了测试,我添加了一个 1ms 的超时来等待变异观察者启动:``` const timeout = (ms) => { return new Promise((resolve) => setTimeout(resolve,小姐)); };等待超时(1); ```
      【解决方案6】:

      TypeScript 用户的补充:

      通过添加一个名为:mutation-observer.d.ts 的文件来声明模块

      /// <reference path="../../node_modules/mutation-observer" />
      declare module "mutation-observer";
      

      然后在你的笑话文件中。

      import MutationObserver from 'mutation-observer'
      (global as any).MutationObserver = MutationObserver
      

      【讨论】:

        【解决方案7】:

        这是对 Matt 的回答 above 的打字稿重写。

        // Test setup
        const mutatationObserverMock = jest
          .fn<MutationObserver, [MutationCallback]>()
          .mockImplementation(() => {
            return {
              observe: jest.fn(),
              disconnect: jest.fn(),
              takeRecords: jest.fn(),
            };
          });
        global.MutationObserver = mutatationObserverMock;
        
        // Usage
        new MutationObserver(() => {
          console.log("lol");
        }).observe(document, {});
        
        // Test
        const observerCb = mutatationObserverMock.mock.calls[0][0];
        observerCb([], mutatationObserverMock.mock.instances[0]);
        
        

        【讨论】:

          猜你喜欢
          • 2017-12-24
          • 2020-11-24
          • 2018-06-10
          • 2020-11-06
          • 2020-04-21
          • 1970-01-01
          • 1970-01-01
          • 2020-12-15
          • 2017-11-23
          相关资源
          最近更新 更多