67

我正在尝试开玩笑地为我的 Web 组件项目编写测试。我已经使用带有 es2015 预设的 babel。加载 js 文件时遇到问题。我遵循了一段代码,其中document对象有一个currentScript对象。但在测试环境中它是null. 所以我想嘲笑同样的事情。但jest.fn()并没有真正的帮助。我该如何处理这个问题?

笑话失败的一段代码。

var currentScriptElement = document._currentScript || document.currentScript;
var importDoc = currentScriptElement.ownerDocument;

我写的测试用例。component.test.js

import * as Component from './sample-component.js';

describe('component test', function() {
  it('check instance', function() {
    console.log(Component);
    expect(Component).toBeDefined();
  });
});

以下是 jest 抛出的错误

Test suite failed to run

    TypeError: Cannot read property 'ownerDocument' of null

      at src/components/sample-component/sample-component.js:4:39

更新: 根据 Andreas Köberle 的建议,我添加了一些全局变量并尝试模拟如下

__DEV__.document.currentScript = document._currentScript = {
  ownerDocument: ''
};
__DEV__.window = {
  document: __DEV__.document
}
__DEV__.document.registerElement = jest.fn();

import * as Component from './arc-sample-component.js';

describe('component test', function() {
  it('check instance', function() {
    console.log(Component);
    expect(Component).toBeDefined();
  });
});

但没有运气

更新:我试过上面没有__dev__. 也可以通过将文档设置为全局。

4

9 回答 9

47

与其他人所说的类似,但不要尝试自己模拟 DOM,只需使用 JSDOM:

// __mocks__/client.js

import { JSDOM } from "jsdom"
const dom = new JSDOM()
global.document = dom.window.document
global.window = dom.window

然后在你的笑话配置中:

    "setupFiles": [
      "./__mocks__/client.js"
    ],
于 2018-05-31T17:32:08.737 回答
22

setUpFiles开玩笑地使用属性解决了这个问题。这将在 jsdom 之后和每个测试之前执行,这对我来说是完美的。

在 Jest 配置中设置 setupFiles,例如:

"setupFiles": ["<rootDir>/browserMock.js"]


// browserMock.js
Object.defineProperty(document, 'currentScript', {
  value: document.createElement('script'),
});

理想的情况是加载 webcomponents.js 来填充 jsdom。

于 2016-12-16T14:18:08.730 回答
15

我一直在为我正在进行的项目模拟文档而苦苦挣扎。我document.querySelector()在 React 组件内部调用,需要确保它正常工作。最终这对我有用:

it('should test something', () => {
  const spyFunc = jest.fn();
  Object.defineProperty(global.document, 'querySelector', { value: spyFunc });
  <run some test>
  expect(spyFunc).toHaveBeenCalled()
});
于 2018-09-27T17:24:02.137 回答
6

如果您需要为属性定义测试值,有一种更精细的方法。每个属性都需要单独定义,并且还需要创建属性writeable

Object.defineProperty(window.document, 'URL', {
  writable: true,
  value: 'someurl'
});

见:https ://github.com/facebook/jest/issues/890

这对我使用 Jest21.2.1和 Node有用v8.11.1

于 2018-05-18T22:49:27.203 回答
6

如果像我一样,您希望将文档模拟为未定义(例如,用于服务器端/客户端测试),我可以在我的测试套件中使用 object.defineProperty 而无需使用 setupFiles

例子:

beforeAll(() => {
  Object.defineProperty(global, 'document', {});
})
于 2018-02-20T10:49:09.057 回答
4

这是我的项目中名为super-project的文件夹中的结构super-project


  • 超级项目
    • 配置
      • __mocks__
        • dom.js
    • 源代码
      • 用户.js
    • 测试
      • user.test.js
    • jest.config.js
    • 包.json

您需要设置 Jest 以在测试中使用模拟:

dom.js

import { JSDOM } from "jsdom"
const dom = new JSDOM()
global.document = dom.window.document
global.window = dom.window

用户.js

export function create() {
  return document.createElement('table');  
}

user.test.js

import { create } from "../src/user";

test('create table', () => {
  expect(create().outerHTML).toBe('<table></table>');
});

jest.config.js

module.exports = {
  setupFiles: ["./config/__mocks__/dom.js"],
};

参考:

您需要创建一个手动模拟:
https ://jestjs.io/docs/en/manual-mocks.html

操作 DOM 对象:
https ://jestjs.io/docs/en/tutorial-jquery

笑话配置:
https ://jestjs.io/docs/en/configuration

于 2020-07-17T18:10:01.653 回答
3

我可以使用 nodejs 上的范围模块来解决同样的问题global,在我的情况下,使用文档模拟设置文档getElementsByClassName

// My simple mock file
export default {
    getElementsByClassName: () => {
        return [{
            className: 'welcome'
        }]
    }
};

// Your test file
import document from './name.component.mock.js';
global.document = {
    getElementsByClassName: document.getElementsByClassName
};
于 2017-01-28T19:38:18.020 回答
2

我找到了另一个解决方案。假设在您的组件内部,您希望通过 className (document.getElementsByClassName) 获取对 DOM 中元素的引用。您可以执行以下操作:

let wrapper
beforeEach(() => {
    wrapper = mount(<YourComponent/>)
    jest.spyOn(document, 'getElementsByClassName').mockImplementation(() => 
        [wrapper.find('.some-class').getDOMNode()]
    )
})

这样,您可以手动将 getElementsByClassName 的返回值设置为等于 .some-class 的引用。可能需要通过调用 wrapper.setProps({}) 重新渲染组件。

希望这对你们中的一些人有所帮助!

于 2020-12-17T10:34:44.547 回答
-2

希望这可以帮助

const wrapper = document.createElement('div');
const render = shallow(<MockComponent{...props} />);
document.getElementById = jest.fn((id) => {
      wrapper.innerHTML = render.find(`#${id}`).html();
      return wrapper;
    });
于 2020-01-02T10:34:48.713 回答