Skip to content

Commit 3ab51c3

Browse files
authored
helper/resource: Additional Go documentation for acceptance testing environment variables and CLI installation behaviors (#890)
Reference: #830 This also removes two unused internal functions from the internal/plugintest package. The missing terraform.io website documentation for this environment variable will be separately submitted as well.
1 parent 5c75b0d commit 3ab51c3

File tree

9 files changed

+152
-61
lines changed

9 files changed

+152
-61
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package resource
2+
3+
// Environment variables for acceptance testing. Additional environment
4+
// variable constants can be found in the internal/plugintest package.
5+
const (
6+
// Environment variable to enable acceptance tests using this package's
7+
// ParallelTest and Test functions whose TestCase does not enable the
8+
// IsUnitTest field. Defaults to disabled, in which each test will call
9+
// (*testing.T).Skip(). Can be set to any value to enable acceptance tests,
10+
// however "1" is conventional.
11+
EnvTfAcc = "TF_ACC"
12+
13+
// Environment variable with hostname for the provider under acceptance
14+
// test. The hostname is the first portion of the full provider source
15+
// address, such as "example.com" in example.com/myorg/myprovider. Defaults
16+
// to "registry.terraform.io".
17+
//
18+
// Only required if any Terraform configuration set via the TestStep
19+
// type Config field includes a provider source, such as the terraform
20+
// configuration block required_providers attribute.
21+
EnvTfAccProviderHost = "TF_ACC_PROVIDER_HOST"
22+
23+
// Environment variable with namespace for the provider under acceptance
24+
// test. The namespace is the second portion of the full provider source
25+
// address, such as "myorg" in registry.terraform.io/myorg/myprovider.
26+
// Defaults to "-" for Terraform 0.12-0.13 compatibility and "hashicorp".
27+
//
28+
// Only required if any Terraform configuration set via the TestStep
29+
// type Config field includes a provider source, such as the terraform
30+
// configuration block required_providers attribute.
31+
EnvTfAccProviderNamespace = "TF_ACC_PROVIDER_NAMESPACE"
32+
)

helper/resource/plugin.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir,
5353
// the host or namespace using environment variables.
5454
var namespaces []string
5555
host := "registry.terraform.io"
56-
if v := os.Getenv("TF_ACC_PROVIDER_NAMESPACE"); v != "" {
56+
if v := os.Getenv(EnvTfAccProviderNamespace); v != "" {
5757
namespaces = append(namespaces, v)
5858
} else {
5959
namespaces = append(namespaces, "-", "hashicorp")
6060
}
61-
if v := os.Getenv("TF_ACC_PROVIDER_HOST"); v != "" {
61+
if v := os.Getenv(EnvTfAccProviderHost); v != "" {
6262
host = v
6363
}
6464

@@ -327,10 +327,10 @@ func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir,
327327
func getProviderAddr(name string) string {
328328
host := "registry.terraform.io"
329329
namespace := "hashicorp"
330-
if v := os.Getenv("TF_ACC_PROVIDER_NAMESPACE"); v != "" {
330+
if v := os.Getenv(EnvTfAccProviderNamespace); v != "" {
331331
namespace = v
332332
}
333-
if v := os.Getenv("TF_ACC_PROVIDER_HOST"); v != "" {
333+
if v := os.Getenv(EnvTfAccProviderHost); v != "" {
334334
host = v
335335
}
336336
return strings.TrimSuffix(host, "/") + "/" +

helper/resource/testing.go

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,27 @@ func AddTestSweepers(name string, s *Sweeper) {
8484
sweeperFuncs[name] = s
8585
}
8686

87+
// TestMain adds sweeper functionality to the "go test" command, otherwise
88+
// tests are executed as normal. Most provider acceptance tests are written
89+
// using the Test() function of this package, which imposes its own
90+
// requirements and Terraform CLI behavior. Refer to that function's
91+
// documentation for additional details.
92+
//
93+
// Sweepers enable infrastructure cleanup functions to be included with
94+
// resource definitions, typically so developers can remove all resources of
95+
// that resource type from testing infrastructure in case of failures that
96+
// prevented the normal resource destruction behavior of acceptance tests.
97+
// Use the AddTestSweepers() function to configure available sweepers.
98+
//
99+
// Sweeper flags added to the "go test" command:
100+
//
101+
// -sweep: Comma-separated list of locations/regions to run available sweepers.
102+
// -sweep-allow-failues: Enable to allow other sweepers to run after failures.
103+
// -sweep-run: Comma-separated list of resource type sweepers to run. Defaults
104+
// to all sweepers.
105+
//
106+
// Refer to the Env prefixed constants for environment variables that further
107+
// control testing functionality.
87108
func TestMain(m interface {
88109
Run() int
89110
}) {
@@ -252,7 +273,8 @@ func runSweeperWithRegion(region string, s *Sweeper, sweepers map[string]*Sweepe
252273
return runE
253274
}
254275

255-
const TestEnvVar = "TF_ACC"
276+
// Deprecated: Use EnvTfAcc instead.
277+
const TestEnvVar = EnvTfAcc
256278

257279
// TestCheckFunc is the callback type used with acceptance tests to check
258280
// the state of a resource. The state passed in is the latest state known,
@@ -275,6 +297,9 @@ type ErrorCheckFunc func(error) error
275297
//
276298
// When the destroy plan is executed, the config from the last TestStep
277299
// is used to plan it.
300+
//
301+
// Refer to the Env prefixed constants for environment variables that further
302+
// control testing functionality.
278303
type TestCase struct {
279304
// IsUnitTest allows a test to run regardless of the TF_ACC
280305
// environment variable. This should be used with care - only for
@@ -376,6 +401,9 @@ type ExternalProvider struct {
376401
// Multiple TestSteps can be sequenced in a Test to allow testing
377402
// potentially complex update logic. In general, simply create/destroy
378403
// tests will only need one step.
404+
//
405+
// Refer to the Env prefixed constants for environment variables that further
406+
// control testing functionality.
379407
type TestStep struct {
380408
// ResourceName should be set to the name of the resource
381409
// that is being tested. Example: "aws_instance.foo". Various test
@@ -508,11 +536,13 @@ type TestStep struct {
508536
}
509537

510538
// ParallelTest performs an acceptance test on a resource, allowing concurrency
511-
// with other ParallelTest.
539+
// with other ParallelTest. The number of concurrent tests is controlled by the
540+
// "go test" command -parallel flag.
512541
//
513542
// Tests will fail if they do not properly handle conditions to allow multiple
514543
// tests to occur against the same resource or service (e.g. random naming).
515-
// All other requirements of the Test function also apply to this function.
544+
//
545+
// Test() function requirements and documentation also apply to this function.
516546
func ParallelTest(t testing.T, c TestCase) {
517547
t.Helper()
518548
t.Parallel()
@@ -529,16 +559,37 @@ func ParallelTest(t testing.T, c TestCase) {
529559
// the "-test.v" flag) is set. Because some acceptance tests take quite
530560
// long, we require the verbose flag so users are able to see progress
531561
// output.
562+
//
563+
// Use the ParallelTest() function to automatically set (*testing.T).Parallel()
564+
// to enable testing concurrency. Use the UnitTest() function to automatically
565+
// set the TestCase type IsUnitTest field.
566+
//
567+
// This function will automatically find or install Terraform CLI into a
568+
// temporary directory, based on the following behavior:
569+
//
570+
// - If the TF_ACC_TERRAFORM_PATH environment variable is set, that Terraform
571+
// CLI binary is used if found and executable. If not found or executable,
572+
// an error will be returned unless the TF_ACC_TERRAFORM_VERSION environment
573+
// variable is also set.
574+
// - If the TF_ACC_TERRAFORM_VERSION environment variable is set, install and
575+
// use that Terraform CLI version.
576+
// - If both the TF_ACC_TERRAFORM_PATH and TF_ACC_TERRAFORM_VERSION environment
577+
// variables are unset, perform a lookup for the Terraform CLI binary based
578+
// on the operating system PATH. If not found, the latest available Terraform
579+
// CLI binary is installed.
580+
//
581+
// Refer to the Env prefixed constants for additional details about these
582+
// environment variables, and others, that control testing functionality.
532583
func Test(t testing.T, c TestCase) {
533584
t.Helper()
534585

535586
// We only run acceptance tests if an env var is set because they're
536587
// slow and generally require some outside configuration. You can opt out
537588
// of this with OverrideEnvVar on individual TestCases.
538-
if os.Getenv(TestEnvVar) == "" && !c.IsUnitTest {
589+
if os.Getenv(EnvTfAcc) == "" && !c.IsUnitTest {
539590
t.Skip(fmt.Sprintf(
540591
"Acceptance tests skipped unless env '%s' set",
541-
TestEnvVar))
592+
EnvTfAcc))
542593
return
543594
}
544595

@@ -621,6 +672,8 @@ func testProviderConfig(c TestCase) (string, error) {
621672
// UnitTest is a helper to force the acceptance testing harness to run in the
622673
// normal unit test suite. This should only be used for resource that don't
623674
// have any external dependencies.
675+
//
676+
// Test() function requirements and documentation also apply to this function.
624677
func UnitTest(t testing.T, c TestCase) {
625678
t.Helper()
626679

helper/resource/testing_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
)
1919

2020
func init() {
21-
if err := os.Setenv(TestEnvVar, "1"); err != nil {
21+
if err := os.Setenv(EnvTfAcc, "1"); err != nil {
2222
panic(err)
2323
}
2424
}

internal/plugintest/config.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ type Config struct {
3030
// DiscoverConfig uses environment variables and other means to automatically
3131
// discover a reasonable test helper configuration.
3232
func DiscoverConfig(sourceDir string) (*Config, error) {
33-
tfVersion := strings.TrimPrefix(os.Getenv("TF_ACC_TERRAFORM_VERSION"), "v")
34-
tfPath := os.Getenv("TF_ACC_TERRAFORM_PATH")
33+
tfVersion := strings.TrimPrefix(os.Getenv(EnvTfAccTerraformVersion), "v")
34+
tfPath := os.Getenv(EnvTfAccTerraformPath)
3535

36-
tempDir := os.Getenv("TF_ACC_TEMP_DIR")
36+
tempDir := os.Getenv(EnvTfAccTempDir)
3737
tfDir, err := ioutil.TempDir(tempDir, "plugintest-terraform")
3838
if err != nil {
3939
return nil, fmt.Errorf("failed to create temp dir: %w", err)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package plugintest
2+
3+
// Environment variables
4+
const (
5+
// Environment variable with acceptance testing temporary directory for
6+
// testing files and Terraform CLI installation, if installation is
7+
// required. By default, the operating system temporary directory is used.
8+
//
9+
// Setting TF_ACC_TERRAFORM_PATH does not override this value for Terraform
10+
// CLI installation, if installation is required.
11+
EnvTfAccTempDir = "TF_ACC_TEMP_DIR"
12+
13+
// Environment variable with path to save Terraform logs during acceptance
14+
// testing. This value sets TF_LOG_PATH in a safe manner when executing
15+
// Terraform CLI commands, which would otherwise be ignored since it could
16+
// interfere with how the underlying execution is performed.
17+
EnvTfAccLogPath = "TF_ACC_LOG_PATH"
18+
19+
// Environment variable with acceptance testing Terraform CLI version to
20+
// download from releases.hashicorp.com, checksum verify, and install. The
21+
// value can be any valid Terraform CLI version, such as 1.1.6, with or
22+
// without a prepended v character.
23+
//
24+
// Setting this value takes precedence over using an available Terraform
25+
// binary in the operation system PATH, or if not found, installing the
26+
// latest version according to checkpoint.hashicorp.com.
27+
//
28+
// By default, the binary is installed in the operating system temporary
29+
// directory, however that directory can be overridden with the
30+
// TF_ACC_TEMP_DIR environment variable.
31+
//
32+
// If TF_ACC_TERRAFORM_PATH is also set, this installation method is
33+
// only invoked when a binary does not exist at that path. No version
34+
// checks are performed against an existing TF_ACC_TERRAFORM_PATH.
35+
EnvTfAccTerraformVersion = "TF_ACC_TERRAFORM_VERSION"
36+
37+
// Acceptance testing path to Terraform CLI binary.
38+
//
39+
// Setting this value takes precedence over using an available Terraform
40+
// binary in the operation system PATH, or if not found, installing the
41+
// latest version according to checkpoint.hashicorp.com. This value does
42+
// not override TF_ACC_TEMP_DIR for Terraform CLI installation, if
43+
// installation is required.
44+
//
45+
// If TF_ACC_TERRAFORM_VERSION is not set, the binary must exist and be
46+
// executable, or an error will be returned.
47+
//
48+
// If TF_ACC_TERRAFORM_VERSION is also set, that Terraform CLI version
49+
// will be installed if a binary is not found at the given path. No version
50+
// checks are performed against an existing binary.
51+
EnvTfAccTerraformPath = "TF_ACC_TERRAFORM_PATH"
52+
)

internal/plugintest/guard.go

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,8 @@ package plugintest
22

33
import (
44
"fmt"
5-
"os"
6-
"testing"
75
)
86

9-
// AcceptanceTest is a test guard that will produce a log and call SkipNow on
10-
// the given TestControl if the environment variable TF_ACC isn't set to
11-
// indicate that the caller wants to run acceptance tests.
12-
//
13-
// Call this immediately at the start of each acceptance test function to
14-
// signal that it may cost money and thus requires this opt-in enviromment
15-
// variable.
16-
//
17-
// For the purpose of this function, an "acceptance test" is any est that
18-
// reaches out to services that are not directly controlled by the test program
19-
// itself, particularly if those requests may lead to service charges. For any
20-
// system where it is possible and realistic to run a local instance of the
21-
// service for testing (e.g. in a daemon launched by the test program itself),
22-
// prefer to do this and _don't_ call AcceptanceTest, thus allowing tests to be
23-
// run more easily and without external cost by contributors.
24-
func AcceptanceTest(t TestControl) {
25-
t.Helper()
26-
if os.Getenv("TF_ACC") != "" {
27-
t.Log("TF_ACC is not set")
28-
t.SkipNow()
29-
}
30-
}
31-
32-
// LongTest is a test guard that will produce a log and call SkipNow on the
33-
// given TestControl if the test harness is currently running in "short mode".
34-
//
35-
// What is considered a "long test" will always be pretty subjective, but test
36-
// implementers should think of this in terms of what seems like it'd be
37-
// inconvenient to run repeatedly for quick feedback while testing a new feature
38-
// under development.
39-
//
40-
// When testing resource types that always take several minutes to complete
41-
// operations, consider having a single general test that covers the basic
42-
// functionality and then mark any other more specific tests as long tests so
43-
// that developers can quickly smoke-test a particular feature when needed
44-
// but can still run the full set of tests for a feature when needed.
45-
func LongTest(t TestControl) {
46-
t.Helper()
47-
if testing.Short() {
48-
t.Log("skipping long test because of short mode")
49-
t.SkipNow()
50-
}
51-
}
52-
537
// TestControl is an interface requiring a subset of *testing.T which is used
548
// by the test guards and helpers in this package. Most callers can simply
559
// pass their *testing.T value here, but the interface allows other

internal/plugintest/helper.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func AutoInitHelper(sourceDir string) (*Helper, error) {
6565
// behind in the system's temporary directory. There is currently no way to
6666
// automatically clean those up.
6767
func InitHelper(config *Config) (*Helper, error) {
68-
tempDir := os.Getenv("TF_ACC_TEMP_DIR")
68+
tempDir := os.Getenv(EnvTfAccTempDir)
6969
baseDir, err := ioutil.TempDir(tempDir, "plugintest")
7070
if err != nil {
7171
return nil, fmt.Errorf("failed to create temporary directory for test helper: %s", err)

internal/plugintest/working_dir.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func (wd *WorkingDir) SetConfig(cfg string) error {
9999
return err
100100
}
101101

102-
if p := os.Getenv("TF_ACC_LOG_PATH"); p != "" {
102+
if p := os.Getenv(EnvTfAccLogPath); p != "" {
103103
if err := wd.tf.SetLogPath(p); err != nil {
104104
return fmt.Errorf("unable to set log path: %w", err)
105105
}

0 commit comments

Comments
 (0)