如何使用 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。

  1. 它会让你的测试更快。
  2. 它会让你的文字更加稳定。
  3. 它将着重于测试要测试的内容。 (API / Internet 连接与测试模板的创建无关)

我相信如果不必“等待”异步响应,您的测试将工作正常。