Vitest 源码解读(二)———— suite 和 test 实现
如果想了解以及执行 suite 和 test 究竟会发生什么,我们就需要先来看一下,suite 和 test 是如何实现的。
describe 是 suite 的别名,我们来看下 suite 实现:
export const suite = createSuite()
export const defaultSuite = suite('')
function createSuite(name, factory) {
const tasks = [];
let suite = {
id: '',
type: 'suite',
name,
mode,
// 在collect 中赋值
tasks: [],
}
// 实现 test 方法
const test = function(name, fn, timeout) {
const test = {
id: '',
type: 'test',
name,
mode,
// 在 collect 中赋值
suite: undefined!,
}
setFn(test, fn)
tasks.push(test)
}
// 执行 suite 的 factory 方法,从而收集 test 的 tasks
async function collect(file) {
if (factory) {
await factory(test)
}
suite.file = file
suite.tasks = tasks
tasks.forEach(task => {
task.suite = suite
if (file) {
task.file = file
}
})
return suite
}
const collector = {
type: 'collector',
name,
mode: 'run',
// test 方法
test,
// 执行过的 test 上下文
tasks,
// 执行 suite 的 factory 方法并填充上下文
collect,
// 清除上下文信息
clear,
on: addHook,
}
return collector;
}
可以看到,执行 suite/describe 方法,会返回一个 collector 对象,该对象包含 test、tasks 和 collect 方法。那么这些方法都是在什么时机被调用的呢?我们来看一下主流程的函数:
上文提到 entry.js 中的逻辑如下,该函数用于具体执行测试用例:
async function run(files, config) {
for (const file of files) {
// 收集测试用例
const files = await collectTests([file], config)
// 执行测试用例
await runSuites(files)
}
}
我们来看一下 collectTests 如何实现:
async function collectTests(paths, suite) {
const files = []
for (const filepath of paths) {
// 获取相对路径
await import(filepath)
// 获取需要执行的 suites
const collectors = [defaultSuite, ...context.suites]
const suites: Suite[] = []
const file: File = {
filepath,
suites: [],
}
// 执行 suites 的 collect 方法
for (const c of collectors) {
context.currentSuite = c
suites.push(await c.collect(file))
}
file.suites = suites
files.push(file)
}
// 返回收集到的上下文信息
return files;
}
收集完毕后,我们来看一下是如何执行的:
for (const file of files) {
for (const suite of file.suites) {
for (const t of suite.tasks)
await getFn(task)
}
}
如何实现链式调用呢, 如:
- test.skip.concurrent
- test.only.concurrent
function createChainable(keys, fn) {
function create(obj) {
const chain = function (...args) {
return fn.apply(obj, args)
}
for (const key of keys) {
Object.defineProperty(chain, key, {
get() {
return create({...obj, [key]: true})
}
})
}
return chain
}
const chain = create({})
chain.fn = fn
return chain
}