轻松写测试:编写 JSX 直接生成 DOM 元素

quote

"I didn't fail the test, I just found 100 ways to do it wrong."

Benjamin Franklin

在编写测试时有时候我们需要生成大量不同 DOM 元素来进行测试。本文将聊聊几种生成 DOM 元素的方式以及它们的优劣势。

首先我们以一个基本的 HTML 作为例子,这是我们的目标:

<!DOCTYPE html>
<html>
<body>
  <div id="root">
    <h1 class="title">Test Title</h1>
    <p>Test Paragraph <a href="https://blog.crimx.com">Test Link</a> End</p>
  </div>
</body>
</html>

DOM APIs

创建 DOM 元素最直接的方式是通过 DOM APIs。

const root = document.createElement('div')
root.id = 'root'

const title = document.createElement('h1')
title.textContent = 'Test Title'
root.appendChild(title)

const p = document.createElement('p')
root.appendChild(p)

p.appendChild(document.createTextNode('Test Paragraph '))

const a = document.createElement('a')
a.href = 'https://blog.crimx.com'
a.textContent = 'Test Link'
p.appendChild(a)

p.appendChild(document.createTextNode(' End'))

document.body.appendChild(root)

优点

  • 速度快。

缺点

  • 编写较繁琐。
  • 不直观。即便使用 document fragment 可以方便离线操作 DOM,光看代码还是不容易看出目标 HTML 是什么样子。

HTML 字符串

另一个最常见的方式是编写 HTML 字符串,通过 innerHTML 解析生成 DOM 元素。

const root = document.createElement('div')
root.id = 'root'
root.innerHTML = `
<h1 class="title">Test Title</h1>
<p>Test Paragraph <a href="https://blog.crimx.com">Test Link</a> End</p>
`
document.body.appendChild(root)

优点

  • 容易编写。
  • 直观。

缺点

  • 需要解析字符串,所以会有性能损失,但这个差别非常小(一般最多只有几毫秒),对于测试来说完全可以接受。
  • 不好查错。字符串不容易 lint,出现错误需要人眼排查。

JSX

这个问题其实正是 React 团队创造 JSX 语法糖的原因。利用 JSX 我们可以在 JS 文件中编写 Markup,然后转换成相应的 JavaScript 代码。

默认情况下,JSX 生成的渲染函数是 React.createElement(component, props, ...children)。然而 JSX 只是语法糖,各种 JSX(TSX) 编译器都提供了选项来自定义生成的函数名。我们完全可以换成直接生成 DOM 的库。

@babel/plugin-transform-react-jsx 提供了 pragma 等选项;TypeScript 可以在 tsconfig.json 中配置 jsxFactory

Babel 例子中配合了 deku 生成 DOM,这个库有点老,写测试也不需要虚拟 DOM 来操作,建议使用更轻量的 tsx-dom(也支持 JSX)。

document.body.appendChild(
  <div id='root'>
    <h1 class='title'>Test Title</h1>
    <p>Test Paragraph <a href='https://blog.crimx.com'>Test Link</a> End</p>
  </div>
)

优点

  • 容易编写。
  • 直观。
  • 可以使用 JSX 相关的 lint 规则。

缺点

  • 需要搭建编译器。虽然如今项目十个有九个都是打包的,但有的脚手架需要 eject 才让配置,这点需要考虑。

总结

如果测试需要大量编写 DOM 元素,使用 JSX 的方式无疑是最方便可靠的;如果只是少量几个测试,可以用 innerHTML 的方式凑合用;一遍不建议使用 DOM APIs ,因为测试首要考虑的应该是直观。