Acceptance Testing
Relevant source files
The following files were used as context for generating this wiki page:- internal/acctest/acctest.go
- internal/service/appconfig/application_test.go
- internal/service/codebuild/project_test.go
- internal/service/codecommit/approval_rule_template_test.go
- internal/service/ec2/instance_test.go
- internal/service/events/target_test.go
- internal/service/lambda/function_test.go
- internal/service/ssm/document_test.go
Acceptance tests are a critical component of the Terraform AWS Provider, designed to verify that the provider's resources function correctly against live AWS APIs. These tests ensure the reliability and correctness of the resource lifecycle operations: Create, Read, Update, and Delete (CRUD), as well as Import. The testing framework is built upon the Terraform Plugin SDK's resource testing package, extended with custom helpers located in the internal/acctest package.
Each acceptance test executes a series of steps that apply Terraform configurations, check the resulting state, and verify the actual infrastructure in AWS. This process validates that the provider accurately models and manages AWS resources. The tests are written in Go and are typically located in _test.go files alongside the resource implementation they are testing.
Test Case Structure
Acceptance tests are defined using the resource.TestCase struct. This struct orchestrates the entire test flow, including pre-checks, provider configuration, and a sequence of test steps.
A typical resource.TestCase is configured with the following key fields:
| Field | Description |
|---|---|
PreCheck | A function to run before the test case. It typically calls acctest.PreCheck(t) to verify AWS credentials and other prerequisites. |
ErrorCheck | A function to check for expected errors during test execution. It often uses acctest.ErrorCheck to handle common AWS API errors. |
Providers | A map of provider names to their factory functions. This is consistently set to acctest.Providers. |
CheckDestroy | A function that runs after the test case is complete to verify that all resources have been successfully destroyed. |
Steps | A slice of resource.TestStep structs, where each step represents a terraform apply and a set of checks. |
Sources: internal/service/lambda/function_test.go:101-107, internal/service/ssm/document_test.go:21-29
Test Lifecycle Flow
The diagram below illustrates the execution flow of a resource.TestCase.
This diagram shows the standard execution flow of an acceptance test, from pre-checks to final resource destruction verification.
Sources: internal/service/appconfig/application_test.go:26-31, internal/service/codebuild/project_test.go:100-105
Core Testing Components
The internal/acctest package provides a suite of shared functions and variables to standardize acceptance tests across the provider.
Key acctest Functions
| Function | Purpose |
|---|---|
PreCheck(t) | Verifies that necessary environment variables (e.g., AWS credentials) are set before running a test. |
ErrorCheck(t, serviceID) | Returns a function that checks for and logs AWS API errors for a given service. |
CheckResourceDisappears(...) | A check function that deletes a resource outside of Terraform and verifies that the next plan is not empty. |
MatchResourceAttrRegionalARN(...) | A check function to verify that a resource attribute matches the expected ARN format for the current region and partition. |
CheckResourceAttrRegionalARN(...) | A check function to verify an ARN attribute, automatically handling partition, region, and account ID. |
Sources: internal/acctest/acctest.go, internal/service/appconfig/application_test.go:64-67, internal/service/ssm/document_test.go:32-34
Provider Configuration
All acceptance tests use a shared provider configuration defined by acctest.Providers. This ensures that tests run with a consistent and correctly configured AWS provider instance.
// internal/service/codecommit/approval_rule_template_test.go:21
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, codecommit.EndpointsID),
Providers: acctest.Providers,
CheckDestroy: testAccCheckCodeCommitApprovalRuleTemplateDestroy,
// ...
})
Sources: internal/service/codecommit/approval_rule_template_test.go:21-25
Test Steps and Checks
The Steps field of a resource.TestCase contains a slice of resource.TestSteps, which define the actions and verifications for each stage of the test.
resource.TestStep
A TestStep typically includes:
Config: A string containing the Terraform configuration to apply.Check: Aresource.TestCheckFuncused to verify the state of the resources after the apply.resource.ComposeTestCheckFuncis often used to chain multiple checks together.ExpectNonEmptyPlan: A boolean that, when true, asserts that the plan after the step is not empty. This is useful for "disappears" tests.ResourceName,ImportState,ImportStateVerify: Fields used to test the resource's import functionality.
The following sequence diagram shows the interactions during a single test step.
This diagram shows how the test runner drives Terraform to apply a configuration, and then directly uses the provider's meta-interface to run checks against the live AWS resources.
Sources: internal/service/lambda/function_test.go:106-116, internal/service/ssm/document_test.go:30-41
Common Test Scenarios
Create, Update, and Import
Most resources have a multi-step test that covers creation, updates, and import.
- Create: The first step applies a basic configuration and checks that the resource was created with the correct attributes.
- Update: Subsequent steps apply modified configurations to test updates to various attributes.
- Import: A final step often includes
ImportState: trueto verify that the resource can be correctly imported into Terraform's state management.
// internal/service/appconfig/application_test.go:88-115
func TestAccAppConfigApplication_updateName(t *testing.T) {
// ...
resource.ParallelTest(t, resource.TestCase{
// ...
Steps: []resource.TestStep{
{
Config: testAccApplicationNameConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckApplicationExists(resourceName),
),
},
{
Config: testAccApplicationNameConfig(rNameUpdated),
Check: resource.ComposeTestCheckFunc(
testAccCheckApplicationExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "name", rNameUpdated),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
Sources: internal/service/appconfig/application_test.go:88-115
"Disappears" Test
This special test case verifies that Terraform can recover when a managed resource is deleted manually or outside of its control.
- A resource is created and verified.
- A custom check function,
acctest.CheckResourceDisappears, is used. It deletes the resource via the AWS SDK. ExpectNonEmptyPlan: trueis set to assert that the subsequentterraform plandetects the missing resource and plans to recreate it.
// internal/service/codecommit/approval_rule_template_test.go:48-63
func TestAccCodeCommitApprovalRuleTemplate_disappears(t *testing.T) {
// ...
resource.ParallelTest(t, resource.TestCase{
// ...
Steps: []resource.TestStep{
{
Config: testAccCodeCommitApprovalRuleTemplate_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckCodeCommitApprovalRuleTemplateExists(resourceName),
acctest.CheckResourceDisappears(acctest.Provider, tfcodecommit.ResourceApprovalRuleTemplate(), resourceName),
),
ExpectNonEmptyPlan: true,
},
},
})
}
Sources: internal/service/codecommit/approval_rule_template_test.go:48-63
Custom Check Functions
While the resource package provides many standard check functions (e.g., TestCheckResourceAttr), tests frequently define their own functions to verify resource state directly against the AWS API.
testAccCheck...Exists
This function type verifies that a resource exists in AWS after creation. It typically uses the AWS SDK's Describe or Get method for the corresponding service.
// internal/service/ssm/document_test.go:618-634
func testAccCheckDocumentExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No SSM Document ID is set")
}
conn := acctest.Provider.Meta().(*conns.AWSClient).SSMConn
_, err := conn.DescribeDocument(&ssm.DescribeDocumentInput{
Name: aws.String(rs.Primary.ID),
})
return err
}
}
Sources: internal/service/ssm/document_test.go:618-634
testAccCheck...Destroy
This function is passed to CheckDestroy and verifies that the resource has been successfully removed from AWS. It calls the same Describe or Get method but expects a NotFound error.
// internal/service/events/target_test.go:440-455
func testAccCheckTargetDestroy(s *terraform.State) error {
conn := acctest.Provider.Meta().(*conns.AWSClient).EventsConn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_cloudwatch_event_target" {
continue
}
t, err := tfevents.FindTarget(conn, rs.Primary.Attributes["event_bus_name"], rs.Primary.Attributes["rule"], rs.Primary.Attributes["target_id"])
if err == nil {
return fmt.Errorf("EventBridge Target %q still exists: %s",
rs.Primary.ID, t)
}
}
return nil
}
Sources: internal/service/events/target_test.go:440-455
Summary
The acceptance testing framework is a cornerstone of the provider's development process. By leveraging the Terraform Plugin SDK and a set of standardized local helpers, developers can write robust tests that validate resource behavior against live AWS services. These tests ensure that creating, updating, importing, and destroying resources works as expected, which is essential for maintaining the high quality and reliability of the Terraform AWS Provider.