AdonisJS v7 已完成功能开发,正在进行最终验证。 了解更多

控制台测试

控制台测试

命令行测试是指测试应用程序或包代码库中的自定义 Ace 命令。

在本指南中,我们将学习如何为命令编写测试、模拟记录器输出以及捕获 CLI 提示。

基本示例

让我们开始创建一个名为 greet 的新命令。

node ace make:command greet
import { BaseCommand } from '@adonisjs/core/ace'
import { CommandOptions } from '@adonisjs/core/types/ace'
export default class Greet extends BaseCommand {
static commandName = 'greet'
static description = 'Greet a username by name'
static options: CommandOptions = {}
async run() {
this.logger.info('Hello world from "Greet"')
}
}

让我们在 tests/unit 目录中创建一个 单元 测试。如果尚未定义,请随时 定义单元测试套件

node ace make:test commands/greet --suite=unit
# DONE: create tests/unit/commands/greet.spec.ts

让我们打开新创建的文件并编写以下测试。我们将使用 ace 服务创建 Greet 命令的实例,并断言它成功退出。

import { test } from '@japa/runner'
import Greet from '#commands/greet'
import ace from '@adonisjs/core/services/ace'
test.group('Commands greet', () => {
test('should greet the user and finish with exit code 0', async () => {
/**
* 创建 Greet 命令类的实例
*/
const command = await ace.create(Greet, [])
/**
* 执行命令
*/
await command.exec()
/**
* 断言命令退出且状态码为 0
*/
command.assertSucceeded()
})
})

让我们使用以下 ace 命令运行测试。

node ace test --files=commands/greet

测试记录器输出

Greet 命令当前将日志消息写入终端。为了捕获此消息并为其编写断言,我们将不得不将 ace 的 UI 库切换到 raw 模式。

raw 模式下,ace 不会将任何日志写入终端。相反,将它们保留在内存中以编写断言。

我们将使用 Japa each.setup 钩子切换进出 raw 模式。

test.group('Commands greet', (group) => {
group.each.setup(() => {
ace.ui.switchMode('raw')
return () => ace.ui.switchMode('normal')
})
// 测试代码在这里
})

定义钩子后,你可以按如下方式更新测试。

test('should greet the user and finish with exit code 1', async () => {
/**
* 创建 Greet 命令类的实例
*/
const command = await ace.create(Greet, [])
/**
* 执行命令
*/
await command.exec()
/**
* 断言命令退出且状态码为 0
*/
command.assertSucceeded()
/**
* 断言命令打印了以下日志消息
*/
command.assertLog('[ blue(info) ] Hello world from "Greet"')
})

测试表格输出

与测试日志消息类似,你可以通过将 UI 库切换到 raw 模式来为表格输出编写断言。

async run() {
const table = this.ui.table()
table.head(['Name', 'Email'])
table.row(['Harminder Virk', 'virk@adonisjs.com'])
table.row(['Romain Lanz', 'romain@adonisjs.com'])
table.row(['Julien-R44', 'julien@adonisjs.com'])
table.row(['Michaël Zasso', 'targos@adonisjs.com'])
table.render()
}

鉴于上面的表格,你可以按如下方式为其编写断言。

const command = await ace.create(Greet, [])
await command.exec()
command.assertTableRows([
['Harminder Virk', 'virk@adonisjs.com'],
['Romain Lanz', 'romain@adonisjs.com'],
['Julien-R44', 'julien@adonisjs.com'],
['Michaël Zasso', 'targos@adonisjs.com'],
])

捕获提示

由于 提示 会阻塞终端等待手动输入,因此在编写测试时必须以编程方式捕获并响应它们。

提示是使用 prompt.trap 方法捕获的。该方法接受提示标题(区分大小写),并提供用于配置其他行为的可链接 API。

在触发提示后,捕获会自动删除。如果测试完成而没有触发带有捕获的提示,则会抛出错误。

在以下示例中,我们在标题为 "What is your name?" 的提示上放置一个捕获,并使用 replyWith 方法回答它。

const command = await ace.create(Greet, [])
command.prompt
.trap('What is your name?')
.replyWith('Virk')
await command.exec()
command.assertSucceeded()

选择选项

你可以使用 chooseOptionchooseOptions 方法通过选择或多选提示来选择选项。

command.prompt
.trap('Select package manager')
.chooseOption(0)
command.prompt
.trap('Select database manager')
.chooseOptions([1, 2])

接受或拒绝确认提示

你可以使用 toggleconfirm 方法接受或拒绝显示的提示。

command.prompt
.trap('Want to delete all files?')
.accept()
command.prompt
.trap('Want to delete all files?')
.reject()

针对验证进行断言

要测试提示的验证行为,可以使用 assertPassesassertFails 方法。这些方法接受提示的值并针对 提示的验证 方法对其进行测试。

command.prompt
.trap('What is your name?')
// 断言提供空值时提示失败
.assertFails('', 'Please enter your name')
command.prompt
.trap('What is your name?')
.assertPasses('Virk')

以下是一起使用断言和回复提示的示例。

command.prompt
.trap('What is your name?')
.assertFails('', 'Please enter your name')
.assertPasses('Virk')
.replyWith('Romain')

可用的断言

以下是命令实例上可用的断言方法列表。

assertSucceeded

断言命令退出且 exitCode=0

await command.exec()
command.assertSucceeded()

assertFailed

断言命令退出且 exitCode 非零。

await command.exec()
command.assertFailed()

assertExitCode

断言命令退出且具有特定的 exitCode

await command.exec()
command.assertExitCode(2)

assertNotExitCode

断言命令以任何 exitCode 退出,但不是给定的退出代码。

await command.exec()
command.assertNotExitCode(0)

assertLog

断言命令使用 this.logger 属性写入日志消息。你可以选择将输出流断言为 stdoutstderr

await command.exec()
command.assertLog('Hello world from "Greet"')
command.assertLog('Hello world from "Greet"', 'stdout')

assertLogMatches

断言命令写入与给定正则表达式匹配的日志消息。

await command.exec()
command.assertLogMatches(/Hello world/)

assertTableRows

断言命令将表格打印到 stdout。你可以提供表格行作为列数组。列表示为单元格数组。

await command.exec()
command.assertTableRows([
['Harminder Virk', 'virk@adonisjs.com'],
['Romain Lanz', 'romain@adonisjs.com'],
['Julien-R44', 'julien@adonisjs.com'],
])