使用Jest监视或模拟document.createElement

时间:2019-07-18 06:58:03

标签: javascript jestjs

我需要测试一些在分配了URL的文档上创建元素然后单击的代码。

我正在使用Jest。

const link = document.createElement('a')

我放弃了模拟文档的尝试,因为我看不到一种简单的方法来进行模拟,尽管模拟点击会很不错。

我需要知道createElement发生了,所以我决定创建一个间谍:

jest.spyOn(document, 'createElement')

由于某种原因,间谍无法通过测试,并且收到与尝试模拟文档时相同的错误:

Error:  TypeError: Cannot set property 'href' of undefined

document.createElement下面的代码是:

link.href = url

有什么想法吗?

2 个答案:

答案 0 :(得分:0)

这是解决方案,我使用node.js运行时环境进行演示:

class Link {
  private name: string;
  private _href: string = '';
  constructor(name) {
    this.name = name;
  }

  get href() {
    return this._href;
  }

  set href(url) {
    this._href = url;
  }
}

const document = {
  createElement(name) {
    return new Link(name);
  }
};

function createLink(url) {
  const link = document.createElement('a');
  link.href = url;
  return link;
}

export { createLink, document, Link };

单元测试:

import { createLink, document, Link } from './';

describe('createLink', () => {
  it('t1', () => {
    const url = 'https://github.com/mrdulin';
    jest.spyOn(document, 'createElement').mockReturnValueOnce(new Link('mock link'));
    const link = createLink(url);
    expect(link).toBeInstanceOf(Link);
    expect(link.href).toBe(url);
  });
});

 PASS  src/stackoverflow/57088724/index.spec.ts
  createLink
    ✓ t1 (6ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.451s, estimated 3s

答案 1 :(得分:0)

在我最新的开源项目 (TabMerger) 上工作时,我想出了一个更简单的解决方案,并认为它更直观,可以帮助正在苦苦挣扎的人。此外,它可以很容易地根据您的需要进行调整。

// function to test
function exportJSON() {
  chrome.storage.local.get("groups", (local) => {
    var group_blocks = local.groups;
    chrome.storage.sync.get("settings", (sync) => {
      group_blocks["settings"] = sync.settings;
 
      var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(group_blocks, null, 2));
      
      // want to test all of below also!!
      var anchor = document.createElement("a");
      anchor.setAttribute("href", dataStr);
      anchor.setAttribute("download", AppHelper.outputFileName() + ".json");
      anchor.click();
      anchor.remove();
    });
  });
}

为了测试,我只是模拟 document.createElement 函数的返回值以返回一个可以被监视的锚对象:

describe("exportJSON", () => {
  it("correctly exports a JSON file of the current configuration", () => {
    // ARRANGE
    // creates the anchor tag as an object
    function makeAnchor(target) {
      return {
        target,
        setAttribute: jest.fn((key, value) => (target[key] = value)),
        click: jest.fn(),
        remove: jest.fn(),
      };
    }
    
    // spy on the document.createElement and mock its return
    // also spy on each anchor function you want to test
    var anchor = makeAnchor({ href: "#", download: "" });
    var createElementMock = jest.spyOn(document, "createElement").mockReturnValue(anchor);
    var setAttributeSpy = jest.spyOn(anchor, "setAttribute");
    var clickSpy = jest.spyOn(anchor, "click");
    var removeSpy = jest.spyOn(anchor, "remove");
     
    // create expectations
    var group_blocks = JSON.parse(localStorage.getItem("groups"));
    group_blocks["settings"] = JSON.parse(sessionStorage.getItem("settings"));
    const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(group_blocks, null, 2));

    jest.clearAllMocks(); // start with clean mocks/spies before call is made
    
    // ACT
    AppFunc.exportJSON();
    
    // ASSERT
    // chromeLocalGetSpy, chromeSyncGetSpy defined elsewhere as
    // jest.spyOn(chrome.storage.local, "get") & jest.spyOn(chrome.storage.sync, "get"), respectively.
    expect(chromeLocalGetSpy).toHaveBeenCalledTimes(1);
    expect(chromeLocalGetSpy).toHaveBeenCalledWith("groups", anything);

    expect(chromeSyncGetSpy).toHaveBeenCalledTimes(1);
    expect(chromeSyncGetSpy).toHaveBeenCalledWith("settings", anything);

    expect(document.createElement).toHaveBeenCalledTimes(1);
    expect(document.createElement).toHaveBeenCalledWith("a");

    expect(setAttributeSpy).toHaveBeenCalledTimes(2);
    expect(setAttributeSpy).toHaveBeenNthCalledWith(1, "href", dataStr);
    expect(setAttributeSpy).toHaveBeenNthCalledWith(2, "download", AppHelper.outputFileName() + ".json");

    expect(clickSpy).toHaveBeenCalledTimes(1);
    expect(removeSpy).toHaveBeenCalledTimes(1);

    createElementMock.mockRestore(); // restore document.createElement so that it is unchanged in other tests
  });
});

结果

exportJSON
    √ correctly exports a JSON file of the current configuration (48 ms)

代码

完整代码可在我的 GitHub

您可以看到 localStoragesessionStorage 分别用于模拟 chrome.storage.localchrome.storage.sync API,位于 <rootDir>/tests/__mocks__/chromeMock.js 文件夹中。