👏

Terraform Provider AWSのテストの書き方

2021/12/30に公開

terraform provider awsにおけるテストの書き方について調べたのですが、Terraform自体のテストの書き方じゃなくてproviderの開発の話はあまりみつからなかったのでまとめました。

参考ドキュメントについて

公式のAcceptance Tests
を読んで理解を深めました。

そもそもどういうテストが行われているのか?

プルリクエストを作成するときにはAcceptanceTestがパスしていないといけないのでAcceptanceTestの書き方を主に記載します。
このAcceptanceテストではterraformのコマンドを実際に実行してAWS providerならAWS上にリソースの作成を行っていました。
なので内部ではterraformのスクリプトを用意してリソースの作成を行います。

テストファイルはどれか

terraform provider awsのレポジトリに<対象リソース名>_test.goのような形でテストが用意されています。

どのようにTerraformファイルを作っているのか

公式のTestStepについての説明および
cluster_test.goを見てみると以下のような記述があります。
これがテスト定義になっています。

func TestAccECSCluster_basic(t *testing.T) {
	var cluster1 ecs.Cluster
	rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
	resourceName := "aws_ecs_cluster.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { acctest.PreCheck(t) },
		ErrorCheck:   acctest.ErrorCheck(t, ecs.EndpointsID),
		Providers:    acctest.Providers,
		CheckDestroy: testAccCheckClusterDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccClusterConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckClusterExists(resourceName, &cluster1),
					acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "ecs", fmt.Sprintf("cluster/%s", rName)),
					resource.TestCheckResourceAttr(resourceName, "name", rName),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportStateId:     rName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

ここで

Config: testAccClusterConfig(rName)

と記載があります。
ここが実際にTerraformファイルの作成が行っている箇所です。
実際ファイルの下の方を見てみるとtestAccClusterConfigの定義があり見慣れたTerraformの定義がされています。

func testAccAWSEcsClusterConfig(rName string) string {
	return fmt.Sprintf(`
resource "aws_ecs_cluster" "test" {
  name = %q
}
`, rName)
}

なお名前については

rName := acctest.RandomWithPrefix("tf-acc-test")

と定義しており、テストの実行ごとにランダムな名前を付与して作るようになっている(おそらくリソースの重複なりでテストが失敗するのを避けたいのだろうと推定しています。)

どこのリージョンにリソースは作られるの?

米国西部 (オレゴン)us-west-2
でした

リソースのクリーンアップをおこなうSweepersについて

基本的にはacceptanceテスト終了時にはリソースを削除するが、
リソースの完全削除を行ってくれるようフレームワーク側で
Sweepers
を用意している。
リソースによってはSweepersの記載がないものもあるがテストの失敗時にリソースが残ったりすることもあるので用意したほうがよさそう

実際
S3のSweeperを見てみると

func init() {
	resource.AddTestSweepers("aws_s3_bucket", &resource.Sweeper{
		Name: "aws_s3_bucket",
		F:    testSweepS3Buckets,
		Dependencies: []string{
			"aws_s3_access_point",
			"aws_s3_bucket_object",
		},
	})
}

のように初期化時にSweeperを呼び出していました。

Acceptance Testの実行方法

terraform-provider-awsのプルリクエストを見てみると
https://github.com/hashicorp/terraform-provider-aws/pull/10881
プルリクエストにはacceptance testingをパスした結果を添付する必要があります。

実行方法は

make testacc TEST=./aws TESTARGS='-run=<テスト対象のコンポーネント>

のようにします

なお

make test TEST=./aws TESTARGS='-run=<テスト対象のコンポーネント>

でもテストは実行できますがこちらはリソースの作成をおこなわないテストなので、このテストが通ってもAcceptanceTestが失敗したりしたので最終的には実際にリソース作成を行う必要がありました。

他のAWSリソースを使うテストはどうするのか?

実際にリソースの作成までおこなうように書かれていました。
例えばElastic Beanstalkのテストを見てみるとapplication_version_test.go

func testAccBeanstalkApplicationVersionConfigWithTags(randInt int, tag1, tag2 string) string {
	return fmt.Sprintf(`
resource "aws_s3_bucket" "default" {
  bucket = "tftest.applicationversion.bucket-%[1]d"
}
resource "aws_s3_bucket_object" "default" {
  bucket = aws_s3_bucket.default.id
  key    = "beanstalk/python-v1.zip"
  source = "test-fixtures/python-v1.zip"
}
resource "aws_elastic_beanstalk_application" "default" {
  name        = "tf-test-name-%[1]d"
  description = "tf-test-desc"
}
resource "aws_elastic_beanstalk_application_version" "default" {
  application = aws_elastic_beanstalk_application.default.name
  name        = "tf-test-version-label-%[1]d"
  bucket      = aws_s3_bucket.default.id
  key         = aws_s3_bucket_object.default.id
  tags = {
    firstTag  = "%[2]s"
    secondTag = "%[3]s"
  }
}
`, randInt, tag1, tag2)
}

としてサンプルアプリケーションをデプロイしていました
ちなみにtest-fixtures配下にこれらの入力ファイルはまとめられていました。

まとめ

terraform provider awsにおけるテストの書き方について調べたこと、わかったことをまとめました。
参考になれば幸いです。

Discussion