如何使用 Jest 测试克隆的 HTML 模板和由 JS HTML 元素生成的模板?
How to test cloned HTML templates and generated by JS HTML elements with Jest?
我有以下 HTML:
<div class='t-wrapper' id='t-wrapper'>
<template id="type1">
<div class='t-container t-container__type1'>
<a>
<div class='poster'></div>
<div class='content'>
<div class='title'></div>
<div class='description'></div>
</div>
</a>
</div>
</template>
</div>
我有以下纯 JS 文件 script.js
:
const SETTINGS = {
// some settings
}
class Manager {
constructor(params) {
this.PUBLISHER_ID = params.PUBLISHER_ID
// ...setting other class properties
this.init(this.SELECTOR, this.TYPE)
}
// private methods
#generateElement(obj, el) {
const element = document.createElement(obj.elType || 'div')
// setting attributes and generating children elements
return element
}
#fillData(data, el) {
el.querySelector('a').setAttribute('href', data.url)
el.querySelector('a').setAttribute('target', SETTINGS[data.origin].target)
// ...setting attributes and text content
return el
}
#render(data, type) {
if ('content' in document.createElement('template')) {
// if HTML template is supported by browser
const template = document.querySelector(`#${type}`)
let clone = template.content.cloneNode(true)
clone = this.#fillData(data, clone)
return clone
} else {
// If HTML template not supported, opt to generating elements
let element = this.#generateElement(SETTINGS.types[type].structure)
element.classList.add(`t-container__${type}`)
element = this.#fillData(data, element)
return element
}
}
// end private methods
get requestUrl() {
return 'constructed URL'
}
async getData() {
const data = // fetching data with fetch()
return data
}
init(elementId, type) {
this.getData().then(
function (data) {
if (data.list && data.list.length) {
const fragment = new DocumentFragment()
const wrapper = document.querySelector(`#${elementId}`)
for (const item of data.list) {
const el = this.#render(item, type)
fragment.appendChild(el)
}
wrapper.appendChild(fragment)
}
}.bind(this)
)
}
}
// Defining all neccessary constants
const PUBLISHER_ID = 'some-id'
const API_KEY = 'some-key'
const SOURCE_ID = '123456789'
const COUNT = 6
const SELECTOR = 't-wrapper'
new Manager({ PUBLISHER_ID, API_KEY, SOURCE_ID, COUNT, SELECTOR })
module.exports = { Manager }
基本上,一旦 class Manager
被实例化,它就会调用 init()
方法。此方法从 API 中获取数据,获取后,它会为接收到的 data.list
数组的每个元素生成 HTML 个元素。生成元素时,它首先检查浏览器是否支持 HTML 模板。如果是 - 正在克隆模板并设置属性。如果不是 - 正在使用 document.createElement()
创建元素。生成的 <a>
元素将具有 "target"
属性,该属性取决于其中一项设置 - "_self"
或 "_blank"
一切正常,元素正在以任何一种方式生成。但是,现在我需要用 Jest 测试它们实际上正在生成,并且当单击 link 时,它应该在新的 window/tab 或相同的 window 中打开,取决于设置。
我对 Jest 和测试非常非常陌生。经过一番搜索后,我尝试关注 this example。所以我创建了这个测试:
const { Manager } = require('./script')
const PUBLISHER_ID = 'some-id'
const API_KEY = 'some-key'
const SOURCE_ID = '123456789'
const COUNT = 6
describe('Manager Script', () => {
let createElement
let querySelector
let createObjectURL
beforeAll(() => {
createElement = document.createElement
querySelector = document.querySelector
createObjectURL = window.URL.createObjectURL
})
afterAll(() => {
jest.restoreAllMocks()
document.createElement = createElement
document.querySelector = querySelector
window.URL.createObjectURL = createObjectURL
})
// ...other tests
it('should render elements', () => {
const divEl = { setAttribute: jest.fn(), innerHTML: '' }
const linkEl = { setAttribute: jest.fn(), innerHTML: '' }
const container = { appendChild: jest.fn() }
document.createElement = jest.fn().mockImplementation(tagName => {
switch (tagName) {
case 'div':
return divEl
case 'a':
return linkEl
}
})
document.querySelector = jest.fn().mockRejectedValueOnce(container)
const manager = new Manager({
PUBLISHER_ID,
API_KEY,
SOURCE_ID,
COUNT,
})
expect(document.createElement).toBeCalledTimes(1)
})
})
但是这个测试失败了 Expected number of calls: 1; Received number of calls: 0
我尝试在实例化后调用manager.init()
,尝试将模板支持检查设置为false并直接使用document.createElement()
部分生成,尝试使用beforeEach/afterEach
(如例如)...测试一直失败。
我做错了什么,我该如何让它发挥作用?我怎样才能测试所有这些东西?
一般来说,我认为您应该使用模拟数据而不是从外部获取数据 API。
- 它会让你的测试更快。
- 它会让你的文字更加稳定。
- 它将着重于测试要测试的内容。
(API / Internet 连接与测试模板的创建无关)
我相信如果不必“等待”异步响应,您的测试将工作正常。
我有以下 HTML:
<div class='t-wrapper' id='t-wrapper'>
<template id="type1">
<div class='t-container t-container__type1'>
<a>
<div class='poster'></div>
<div class='content'>
<div class='title'></div>
<div class='description'></div>
</div>
</a>
</div>
</template>
</div>
我有以下纯 JS 文件 script.js
:
const SETTINGS = {
// some settings
}
class Manager {
constructor(params) {
this.PUBLISHER_ID = params.PUBLISHER_ID
// ...setting other class properties
this.init(this.SELECTOR, this.TYPE)
}
// private methods
#generateElement(obj, el) {
const element = document.createElement(obj.elType || 'div')
// setting attributes and generating children elements
return element
}
#fillData(data, el) {
el.querySelector('a').setAttribute('href', data.url)
el.querySelector('a').setAttribute('target', SETTINGS[data.origin].target)
// ...setting attributes and text content
return el
}
#render(data, type) {
if ('content' in document.createElement('template')) {
// if HTML template is supported by browser
const template = document.querySelector(`#${type}`)
let clone = template.content.cloneNode(true)
clone = this.#fillData(data, clone)
return clone
} else {
// If HTML template not supported, opt to generating elements
let element = this.#generateElement(SETTINGS.types[type].structure)
element.classList.add(`t-container__${type}`)
element = this.#fillData(data, element)
return element
}
}
// end private methods
get requestUrl() {
return 'constructed URL'
}
async getData() {
const data = // fetching data with fetch()
return data
}
init(elementId, type) {
this.getData().then(
function (data) {
if (data.list && data.list.length) {
const fragment = new DocumentFragment()
const wrapper = document.querySelector(`#${elementId}`)
for (const item of data.list) {
const el = this.#render(item, type)
fragment.appendChild(el)
}
wrapper.appendChild(fragment)
}
}.bind(this)
)
}
}
// Defining all neccessary constants
const PUBLISHER_ID = 'some-id'
const API_KEY = 'some-key'
const SOURCE_ID = '123456789'
const COUNT = 6
const SELECTOR = 't-wrapper'
new Manager({ PUBLISHER_ID, API_KEY, SOURCE_ID, COUNT, SELECTOR })
module.exports = { Manager }
基本上,一旦 class Manager
被实例化,它就会调用 init()
方法。此方法从 API 中获取数据,获取后,它会为接收到的 data.list
数组的每个元素生成 HTML 个元素。生成元素时,它首先检查浏览器是否支持 HTML 模板。如果是 - 正在克隆模板并设置属性。如果不是 - 正在使用 document.createElement()
创建元素。生成的 <a>
元素将具有 "target"
属性,该属性取决于其中一项设置 - "_self"
或 "_blank"
一切正常,元素正在以任何一种方式生成。但是,现在我需要用 Jest 测试它们实际上正在生成,并且当单击 link 时,它应该在新的 window/tab 或相同的 window 中打开,取决于设置。
我对 Jest 和测试非常非常陌生。经过一番搜索后,我尝试关注 this example。所以我创建了这个测试:
const { Manager } = require('./script')
const PUBLISHER_ID = 'some-id'
const API_KEY = 'some-key'
const SOURCE_ID = '123456789'
const COUNT = 6
describe('Manager Script', () => {
let createElement
let querySelector
let createObjectURL
beforeAll(() => {
createElement = document.createElement
querySelector = document.querySelector
createObjectURL = window.URL.createObjectURL
})
afterAll(() => {
jest.restoreAllMocks()
document.createElement = createElement
document.querySelector = querySelector
window.URL.createObjectURL = createObjectURL
})
// ...other tests
it('should render elements', () => {
const divEl = { setAttribute: jest.fn(), innerHTML: '' }
const linkEl = { setAttribute: jest.fn(), innerHTML: '' }
const container = { appendChild: jest.fn() }
document.createElement = jest.fn().mockImplementation(tagName => {
switch (tagName) {
case 'div':
return divEl
case 'a':
return linkEl
}
})
document.querySelector = jest.fn().mockRejectedValueOnce(container)
const manager = new Manager({
PUBLISHER_ID,
API_KEY,
SOURCE_ID,
COUNT,
})
expect(document.createElement).toBeCalledTimes(1)
})
})
但是这个测试失败了 Expected number of calls: 1; Received number of calls: 0
我尝试在实例化后调用manager.init()
,尝试将模板支持检查设置为false并直接使用document.createElement()
部分生成,尝试使用beforeEach/afterEach
(如例如)...测试一直失败。
我做错了什么,我该如何让它发挥作用?我怎样才能测试所有这些东西?
一般来说,我认为您应该使用模拟数据而不是从外部获取数据 API。
- 它会让你的测试更快。
- 它会让你的文字更加稳定。
- 它将着重于测试要测试的内容。 (API / Internet 连接与测试模板的创建无关)
我相信如果不必“等待”异步响应,您的测试将工作正常。