メインコンテンツにスキップ

コマンド: test

tofu testコマンドを使用すると、実際のインフラストラクチャを作成し、必要な条件(アサーション)が満たされていることを確認することで、OpenTofu構成をテストできます。テストが完了すると、OpenTofuは作成したリソースを破棄します。

使用方法

使用方法: tofu test [オプション].

このコマンドは、カレントディレクトリまたは`tests`というディレクトリにあるすべての`*.tftest.hcl`、`*.tftest.json`、`*.tofutest.hcl`、および`*.tofutest.json`ファイルを実行します。以下のオプションを使用して、この動作をカスタマイズできます。

拡張子の優先順位

同じベース名を持つ`.tftest.hcl`ファイルと`.tofutest.hcl`ファイルの両方がディレクトリに存在する場合、OpenTofuは`.tofutest.hcl`ファイルを優先し、`.tftest.hcl`ファイルを無視します。例えば

  • `main.tftest.hcl`と`main.tofutest.hcl`の両方が同じディレクトリに存在する場合、OpenTofuは`main.tofutest.hcl`のみを読み込み、`main.tftest.hcl`は無視します。

これにより、両方が利用可能な場合、`.tofu`ファイルは常に`.tf`ファイルよりも優先されます。このシナリオは、モジュールがOpenTofuとTerraformの両方をサポートし、それぞれに異なるテストを作成したいモジュール作成者にとって役立ちます。

JSONベースのテストファイルにも同じルールが適用されます

  • `main.tftest.json`と`main.tofutest.json`の両方が同じディレクトリに存在する場合、OpenTofuは`main.tofutest.json`のみを読み込み、`main.tftest.json`は無視します。

オプション

  • `-test-directory=パス` テストディレクトリを設定します(デフォルト: "tests")。OpenTofuは、`tofu test`を実行すると、指定されたディレクトリとカレントディレクトリでテストファイルを検索します。パスは、現在の作業ディレクトリからの相対パスである必要があります。
  • `-filter=テストファイル` 実行する個々のテストファイルを指定します。複数のファイルを指定するには、このオプションを複数回使用します。パスは、現在の作業ディレクトリからの相対パスである必要があります。
  • `-var 'foo=bar'` ルートモジュールの入力変数を設定します。複数の変数を追加するには、このオプションを複数回指定します。
  • `-var-file=ファイル名` 指定されたファイルから複数の変数を設定します。このファイルに加えて、OpenTofuは自動的に`terraform.tfvars`と`*.auto.tfvars`を読み込みます。複数のファイルを指定するには、このオプションを複数回使用します。
  • `-json` 出力形式をJSONに変更します。
  • `-no-color` コマンド出力の色付けを無効にします。
  • `-verbose` 各テストランブロックの実行中に、プランまたは状態を出力します。

ディレクトリ構造

`tofu test`コマンドは、フラットまたはネストの2つのディレクトリレイアウトをサポートしています

このレイアウトでは、`*.tftest.hcl`テストファイルは、テスト対象の`*.tf`ファイルのすぐ横に配置されます。各`*.tf`ファイルに独自のテストファイルが必要というルールはありませんが、従うことをお勧めします。

コードブロック
.
├── main.tf
├── main.tftest.hcl
├── foo.tf
├── foo.tftest.hcl
├── bar.tf
└── bar.tftest.hcl

モジュールのテスト

モジュールをテストする場合、上記のディレクトリ構造のいずれかを各モジュールに使用できます。

このレイアウトでは、tofu test -test-directory=./path/to/module を実行して、対象のモジュールをテストします。

コードブロック
.
├── module1
│ ├── main.tf
│ ├── main.tftest.hcl
│ ├── foo.tf
│ ├── foo.tftest.hcl
│ ├── bar.tf
│ └── bar.tftest.hcl
└── module2
└── ...

*.tftest.hcl / *.tofutest.hcl ファイル構造

OpenTofu のテスト言語は、メインの OpenTofu 言語と似ており、同じブロック構造を使用します。

テストファイルは以下で構成されます。

run ブロック

run ブロックには、tofu apply または tofu plan を実行し、すべての assert ブロックを評価する単一のテストケースが含まれています。テストが完了すると、tofu destroy を使用して一時的に作成されたリソースを削除します。

run ブロックは、以下の要素で構成されます。

名前タイプ説明
assertブロックコード (例: main.tf) がインフラストラクチャを正しく作成したかどうかを確認するアサーションを定義します。 assert ブロックを指定しない場合、OpenTofu はアサーションなしで設定を適用します。
moduleブロックテスト対象のモジュールをオーバーライドします。これを使用して、より精巧なテストのためのヘルパーモジュールを読み込むことができます。
expect_failuresリスト現在の `run` でプロビジョニングに失敗するはずのリソースのリスト。
variablesブロック現在のテストケースの変数を定義します。変数セクションを参照してください。
commandplan または applyOpenTofu が実行するコマンド (plan または apply) を定義します。デフォルトは apply です。
plan_optionsブロックplan または apply 操作のオプション。
providersオブジェクトプロバイダーのエイリアス。
override_resourceブロック実行時にオーバーライドするリソースを定義します。
override_dataブロック実行時にオーバーライドするデータソースを定義します。
override_moduleブロック実行時にオーバーライドするモジュール呼び出しを定義します。

run.assert ブロック

run ブロック内に assert ブロックを指定して、apply または plan 操作の完了後にインフラストラクチャの状態をテストできます。定義できるブロック数に理論的な制限はありません。

各ブロックには、次の 2 つの属性が必要です。

  1. condition は、テストに合格するには true を、テストに失敗するには false を返すOpenTofu 条件です。条件は、メインコードのリソース、データソース、変数、出力、またはモジュールを参照する**必要があります**。そうでない場合、OpenTofu はテストの実行を拒否します。
  2. error_message は、テストが失敗したときに何が起こったかを説明する文字列です。

条件では、現在の OpenTofu 状態の基本的なチェックを実行し、OpenTofu 関数を使用することのみが可能です。**テストコードに直接追加のデータソースを定義することはできません。** この制限を回避するには、module ブロックを使用してヘルパーモジュールを読み込むことができます。

run.module ブロック

条件式で提供されるツールでは、コードがインフラストラクチャを正しく作成したかどうかをテストするには不十分な場合があります。

module ブロックを使用して、tofu test がロードするメインモジュールをオーバーライドできます。これにより、 `assert` 条件で使用できる追加のリソースまたはデータソースを作成できます。

その構文は、通常の OpenTofu コードでモジュールを読み込むのと似ています。

コードブロック
run "test" {
module {
source = "./some-module"
}
}

module ブロックには、次の 2 つの属性があります。

  • source 属性は、ロードするモジュールのディレクトリ、またはその他のモジュールソースを指します。
  • version は、使用するモジュールのバージョンを指定します。

variables ブロックと run.variables ブロック

テスト対象のコード (例: main.tf) には、テストケースから入力する必要がある変数ブロックが含まれていることがよくあります。次のいずれかの方法を使用して、テスト実行に変数を渡すことができます。

順序ソース
1TF_VAR_ プレフィックスが付いた環境変数。
2現在のディレクトリで指定された tfvar ファイル: terraform.tfvars および *.auto.tfvars
3テストディレクトリで指定された tfvar ファイル: tests/terraform.tfvars および tests/*.auto.tfvars
4フラグ `-var` を使用して定義されたコマンドライン変数、およびフラグ `-var-file` で指定されたファイルで定義された変数。
5テストファイルの variables ブロックの変数。
6run ブロックの variables ブロックの変数。

OpenTofu は上記の順序で変数を評価するため、これを使用して以前に設定された変数をオーバーライドできます。例えば

コードブロック
# First, set the variable here:
variables {
name = "OpenTofu"
}

run "basic" {
assert {
condition = output.greeting == "Hello OpenTofu!"
error_message = "Incorrect greeting: ${output.greeting}"
}
}

run "override" {
# Override it for this test case only here:
variables {
name = "OpenTofu user"
}
assert {
condition = output.greeting == "Hello OpenTofu user!"
error_message = "Incorrect greeting: ${output.greeting}"
}
}

run.expect_failures リスト

場合によっては、検証が機能していることを確認するために、コードの意図的なエラーをテストすることがあります。

run ブロック内で expect_failures を使用して、指定されたパラメーターでコードを実行したときにどの変数またはリソースが失敗する必要があるかを指定できます。

たとえば、以下のテストケースでは、負の数が指定された場合に `instances` 変数が正しく検証に失敗するかどうかを確認します。

コードブロック
run "main" {
command = plan

variables {
instances = -1
}

expect_failures = [
var.instances,
]
}

また、`expect_failure` 句を使用して、事前条件や事後条件などのライフサイクルイベント、およびチェックの結果を確認することもできます。

以下の例では、設定ミスのあるヘルスチェックが失敗するかどうかを確認します。これにより、間違ったエンドポイントに対して実行されている場合でも、ヘルスチェックが常に返されないことが保証されます。

コードブロック
run "test-failure" {
variables {
# This healthcheck endpoint won't exist:
health_endpoint = "/nonexistent"
}

expect_failures = [
# We expect this to fail:
check.health
]
}

run.command 設定と run.plan_options ブロック

デフォルトでは、tofu testtofu apply を使用して実際のインフラストラクチャを作成します。場合によっては、たとえば実際のインフラストラクチャが非常に高価であるか、テスト目的で実行できない場合、代わりに `tofu plan` のみを `run` する方が便利な場合があります。 `command = plan` 設定を使用して、適用ではなくプランを実行できます。次の例では、実際にプランを適用せずに、変数が `docker_image` リソースに正しく渡されるかどうかをテストします。

コードブロック
run "test" {
command = plan
plan_options {
refresh = false
}
variables {
image_name = "myapp"
}
assert {
condition = docker_image.build.name == "myapp"
error_message = "Missing build resource"
}
}

command 設定に関係なく、`plan_options` ブロックを使用して、両方のモードで次の追加オプションを指定できます。

名前説明
modeリモートインフラストラクチャからローカル状態のみを更新するには、このオプションを `normal` (デフォルト) から `refresh-only` に変更します。
refresh状態ファイルに関連する外部変更のチェックを無効にするには、このオプションを `false` に設定します。 `tofu plan -refresh=false` と同様です。
replace上記の例の `[docker_image.build]` など、指定されたリソースのリストの強制置換。 `tofu plan -replace=docker_image.build` と同様です。
target計画を指定されたモジュールまたはリソースのリストに制限します。 `tofu plan -target=docker_image.build` と同様です。

providers ブロック

テスト実行のためにプロバイダー設定をオーバーライドしたい場合があります。 run ブロックの外側で provider ブロックを使用して、テストアカウントの資格情報など、プロバイダーに追加の構成オプションを提供できます。

コードブロック
provider "aws" {
// Add additional settings here
}

プロバイダーがサポートしている場合、この機能により部分的または完全にオフラインのテストも有効になります。次の例は、AWSプロバイダーとS3バケットリソースを使用した完全にオフラインのテストを示しています

コードブロック
// Configure the AWS provider to run fake credentials and without
// any validations. Not all providers support this, but when they
// do, you can run fully offline tests.
provider "aws" {
access_key = "foo"
secret_key = "bar"

skip_credentials_validation = true
skip_region_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
}

run "test" {
// Run in plan mode to skip applying:
command = plan

// Disable the refresh to prevent reaching out to the AWS API:
plan_options {
refresh = false
}

// Test if the bucket name is correctly passed to the aws_s3_bucket
// resource:
variables {
bucket_name = "test"
}
assert {
condition = aws_s3_bucket.test.bucket == "test"
error_message = "Incorrect bucket name: ${aws_s3_bucket.test.bucket}"
}
}

プロバイダーエイリアス

プロバイダーのオーバーライドに加えて、プロバイダーにエイリアスを付けて、run ブロック内で別のプロバイダーに置き換えることができます。これは、同じテストファイル内に2つのプロバイダー構成があり、それらを切り替えたい場合に役立ちます。

以下の例では、sockettest テストケースは、ファイルの残りの部分とは異なるDockerプロバイダー構成を読み込みます。

コードブロック
# This is the default "docker" provider for this file:
provider "docker" {
host = "tcp://0.0.0.0:2376"
}

# This will be the override:
provider "docker" {
alias = "unixsocket"
host = "unix:///var/run/docker.sock"
}

run "sockettest" {
# Replace the "docker" provider for this test case only:
providers = {
docker = docker.unixsocket
}

assert {
condition = docker_image.build.name == "myapp"
error_message = "Missing build resource"
}
}

// Add other tests with the original provider here.

mock_provider ブロック

mock_provider ブロックを使用すると、プロバイダー構成をモックされた構成に置き換えることができます。このようなシナリオでは、プロバイダーリソースとデータソースの作成と取得はスキップされます。代わりに、OpenTofuはテストで使用されるすべての計算属性とブロックを自動的に生成します。

モックプロバイダーは、alias フィールドと、mock_resource および mock_data ブロックもサポートしています。場合によっては、mock_resource または mock_data ブロックの defaults フィールド内に渡すことにより、自動的に生成された値の代わりにデフォルト値を使用したい場合があります。

以下の例では、バケットを実際に作成せずに、バケット名がリソースに正しく渡されているかどうかをテストします

コードブロック
// All resources and data sources provided by `aws.mock` provider
// will be mocked. Their values will be automatically generated.
mock_provider "aws" {
alias = "mock"
}

// The same goes for `local` provider. Also, every `local_file`
// data source will have its `content` set to `test`.
mock_provider "local" {
mock_data "local_file" {
defaults = {
content = "test"
}
}
}

// Test if the bucket name is correctly passed to the aws_s3_bucket
// resource from the local file.
run "test" {
// Use `aws.mock` provider for this test run only.
providers = {
aws = aws.mock
}

assert {
condition = aws_s3_bucket.test.bucket == "test"
error_message = "Incorrect bucket name: ${aws_s3_bucket.test.bucket}"
}
}

override_resource および override_data ブロック

場合によっては、特定のリソースまたはデータソースをオーバーライドしてインフラストラクチャをテストしたい場合があります。 override_resource または override_data ブロックを使用して、実際プロバイダーを使用してこれらのリソースまたはデータソースの作成と取得をスキップできます。代わりに、OpenTofuはテストで使用されるすべての計算属性とブロックを自動的に生成します。

これらのブロックは、次の要素で構成されています

名前タイプ説明
target参照必須。オーバーライドされるターゲットリソースまたはデータソースのアドレス。
オブジェクト自動的に生成される値の代わりに使用される、計算属性とブロックのカスタム値。

テストファイル全体または単一の run ブロック内で、override_resource または override_data ブロックを使用できます。同じ target に対して両方が指定されている場合、後者が優先されます。

以下の例では、バケットを実際に作成せずに、バケット名がリソースに正しく渡されているかどうかをテストします

コードブロック
// This data source will not be called for any run
// in this `.tftest.hcl` file. Instead, `values` object
// will be used to populate `content` attribute. Other
// attributes and blocks will be automatically generated.
override_data {
target = data.local_file.bucket_name
values = {
content = "test"
}
}

// Test if the bucket name is correctly passed to the aws_s3_bucket
// resource from the local file.
run "test" {
// S3 bucket will not be created in AWS for this run,
// but it's available to use in both tests and configuration.
override_resource {
target = aws_s3_bucket.test
}

assert {
condition = aws_s3_bucket.test.bucket == "test"
error_message = "Incorrect bucket name: ${aws_s3_bucket.test.bucket}"
}
}

自動生成値

リソースとデータソースのモックには、それぞれのプロバイダーを呼び出さずに、計算属性を自動的に生成するOpenTofuが必要です。これらの値を生成する場合、OpenTofuはカスタムプロバイダーロジックに従うことができないため、値のタイプに基づいた単純なルールを使用します

属性タイプ生成された値
数値0
ブール値false
文字列ランダムな英数字の文字列。
リスト空のリスト。
マップ空のマップ。
セット空のセット。
オブジェクト同じロジックで再帰的にフィールドが埋められたオブジェクト。
タプル空のタプル。

override_module ブロック

場合によっては、特定のモジュール呼び出しをオーバーライドしてインフラストラクチャをテストしたい場合があります。 override_module ブロックを使用して、呼び出されたモジュールによって提供されるすべての構成を無視できます。この場合、OpenTofuは override_module ブロックで指定されたカスタム値をモジュール出力として使用します。

ブロックは、次の要素で構成されています

名前タイプ説明
target参照必須。オーバーライドされるターゲットモジュール呼び出しのアドレス。
出力オブジェクトモジュール呼び出し出力として使用される値。出力が指定されていない場合、OpenTofuはデフォルトで null に設定します。

テストファイル全体または単一の`run`ブロック内で、`override_module`ブロックを使用できます。同じ`target`に対して両方が指定されている場合、後者が優先されます。

以下の例では、モジュールを実際に呼び出さずに、バケット名がモジュールから正しく渡されているかどうかをテストします

コードブロック
// All the module configuration will be ignored for this
// module call. Instead, the `outputs` object will be used
// to populate module outputs.
override_module {
target = module.bucket_meta
outputs = {
name = "test"
tags = {
Environment = "Test Env"
}
}
}

// Test if the bucket name is correctly passed to the aws_s3_bucket
// resource from the module call.
run "test" {
// S3 bucket will not be created in AWS for this run,
// but it's available to use in both tests and configuration.
override_resource {
target = aws_s3_bucket.test
}

assert {
condition = aws_s3_bucket.test.bucket == "test"
error_message = "Incorrect bucket name: ${aws_s3_bucket.test.bucket}"
}

assert {
condition = aws_s3_bucket.test.tags["Environment"] == "Test Env"
error_message = "Incorrect `Environment` tag: ${aws_s3_bucket.test.tags["Environment"]}"
}
}