# 单元测试
Mpx 框架提供了 jest 转换器 mpx-jest,结合微信小程序提供的 miniprogram-simulate (opens new window) 来进行单元测试的工作。
因为目前仅微信提供了仿真工具,暂时只支持微信小程序平台的单元测试。如果需要 E2E 测试,则和框架无关了,可参考微信的小程序自动化 (opens new window)。
如果是初始化项目,单元测试相关的项目依赖和配置可以通过 @mpx/cli 创建项目时选择使用单元测试选项自动生成,如果时旧项目需要使用,可以按照下方步骤安装依赖和添加配置。
# 安装依赖
npm i -D @mpxjs/mpx-jest @mpxjs/miniprogram-simulate jest babel-jest
// 如果项目使用了ts,则还需要安装
npm i -D ts-jest
# jest 相关配置
首先在项目根目录创建 jest.config.js 配置文件,并加入以下关键配置
testEnvironment: 'jsdom', // 使用 jsdom 环境
transform: {
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
'^.+\\.mpx$': '<rootDir>/node_modules/@mpxjs/mpx-jest',
'^.+\\.ts$': '<rootDir>/node_modules/ts-jest' // 如果没使用 ts 可不用添加
},
setupFiles: ['<rootDir>/test/setup'], // test 文件夹下声明 setup,路径可以随意定义,可以为每一个单测添加相应的配置
transformIgnorePatterns: ['node_modules/(?!(@mpxjs))'], // 定义node_modules 中需要进行 transform 的内容
# 简单的断言
暂时进行一个简单的组件单元测试书写,对于复杂组件以及通用测试逻辑的总结我们会在后续进行发布。
示例如下:
<template>
<view>{{ message }}</view>
</template>
<script>
import {createComponent} from '@mpxjs/core'
createComponent({
data: {
message: 'hello!'
},
attached () {
this.message = 'bye!'
}
})
</script>
对应的 hello-world.spec.js
const simulate = require('@mpxjs/miniprogram-simulate')
// 这里是一些 Jasmine 2.0 的测试,你也可以使用你喜欢的任何断言库或测试工具。
describe('MyComponent', () => {
let id
beforeAll(() => {
id = simulate.loadMpx('<rootDir>/src/components/hello-world.mpx')
})
// 检查 mount 中的组件实例
it('correctly sets the message when component attached', () => {
const comp = simulate.render(id)
const instance = comp.instance
// Mpx提供的数据响应是发生在组件挂载时的,未挂载前只能通过实例上的data访问数据
expect(instance.data.message).toBe('hello!')
const parent = document.createElement('parent-wrapper') // 创建容器节点
comp.attach(parent) // 将组件插入到容器节点中,会触发 attached 生命周期
// 挂载后则可以直接通过实例访问
expect(instance.message).toBe('bye!')
})
// 创建一个实例并检查渲染输出
it('renders the correct message', () => {
const comp = simulate.render(id)
const parent = document.createElement('parent-wrapper') // 创建容器节点
comp.attach(parent) // 挂载组件到容器节点
expect(comp.dom.innerHTML).toBe('<wx-view>bye!</wx-view>')
})
})
# 编写可被测试的组件
很多组件的渲染输出由它的 props 决定。事实上,如果一个组件的渲染输出完全取决于它的 props,那么它会让测试变得简单,就好像断言不同参数的纯函数的返回值。看下面这个例子:
<template>
<view>{{ msg }}</view>
</template>
<script>
import {createComponent} from '@mpxjs/core'
createComponent({
properties: { msg: String }
})
</script>
你可以在不同的 properties 中,通过 simulate.render 的第二个参数控制组件的输出:
const simulate = require('@mpxjs/miniprogram-simulate')
// 省略辅助方法
describe('MyComponent', () => {
it('renders correctly with different props', () => {
const id = simulate.loadMpx('<rootDir>/src/components/hello-world.mpx')
const comp1 = simulate.render(id, { msg: 'hello' })
const parent1 = document.createElement('parent-wrapper')
comp1.attach(parent1)
expect(comp1.dom.innerHTML).toBe('<wx-view>hello</wx-view>')
const comp2 = simulate.render(id, { msg: 'bye' })
const parent2 = document.createElement('parent-wrapper')
comp2.attach(parent2)
expect(comp2.dom.innerHTML).toBe('<wx-view>bye</wx-view>')
})
})
# 断言异步更新
小程序视图层的更新是异步的,一些依赖视图更新结果的断言必须 await simulate.sleep() 或者 await comp.instance.$nextTick() 后进行:
const simulate = require('@mpxjs/miniprogram-simulate')
// 省略辅助方法
it('updates the rendered message when vm.message updates', async () => {
const id = simulate.loadMpx('<rootDir>/src/components/hello-world.mpx')
const comp = simulate.render(id)
const parent = document.createElement('parent-wrapper')
comp.attach(parent)
comp.instance.msg = 'foo'
await simulate.sleep(10)
expect(comp.dom.innerHTML).toBe('<wx-view>foo</wx-view>')
})
更深入的 Mpx 单元测试的内容将在以后持续更新……