コンソールテスト
コンソールテストは、アプリケーションまたはパッケージのコードベースに含まれるカスタム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 = 'ユーザー名でユーザーに挨拶する'
  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('ユーザーに挨拶し、終了コード1で終了することをアサートする', 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('ユーザーに挨拶し、終了コード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()
オプションの選択
セレクトまたはマルチセレクトのプロンプトでオプションを選択するには、chooseOptionおよびchooseOptionsメソッドを使用します。
command.prompt
  .trap('Select package manager')
  .chooseOption(0)
command.prompt
  .trap('Select database manager')
  .chooseOptions([1, 2])
確認プロンプトの承認または拒否
toggleメソッドとconfirmメソッドを使用して表示されるプロンプトを承認または拒否できます。
command.prompt
  .trap('Want to delete all files?')
  .accept()
command.prompt
  .trap('Want to delete all files?')
  .reject()
バリデーションのアサーション
プロンプトのバリデーションの動作をテストするために、assertPassesメソッドとassertFailsメソッドを使用できます。これらのメソッドは、プロンプトの値を受け入れ、プロンプトのvalidateメソッドに対してテストを行います。
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.assertSucceeded()
assertExitCode
コマンドが特定のexitCodeで終了したことをアサートします。
await command.exec()
command.assertExitCode(2)
assertNotExitCode
コマンドが任意のexitCodeで終了したことをアサートしますが、指定された終了コードではないことをアサートします。
await command.exec()
command.assertNotExitCode(0)
assertLog
コマンドがthis.loggerプロパティを使用してログメッセージを書き込んだことをアサートします。オプションで出力ストリームをstdoutまたはstderrとしてアサートすることもできます。
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'],
])