Skip to content

Commit 5adf5f1

Browse files
Ivan De Marinobflad
andauthored
Adding resource.TestCheckResourceAttrWith test utility (#950)
* Adding `resource.TestCheckResourceAttrWith` test utility * Adding a test suite for `resource.TestCheckResourceAttrWith` * Adding changelog entry * Adding examples * Apply suggestions from code review Co-authored-by: Brian Flad <[email protected]> * Providing `TestCheckResourceAttrWith` with a more comprehensive godoc Co-authored-by: Brian Flad <[email protected]>
1 parent bb44c87 commit 5adf5f1

File tree

4 files changed

+569
-2
lines changed

4 files changed

+569
-2
lines changed

.changelog/950.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:feature
2+
helper/resource: New `TestCheckResourceAttrWith` test helper, that simplifies checking of attribute values via custom functions
3+
```

helper/resource/testing.go

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ import (
1313
"time"
1414

1515
"github.com/hashicorp/go-multierror"
16-
testing "github.com/mitchellh/go-testing-interface"
16+
"github.com/mitchellh/go-testing-interface"
1717

1818
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
1919
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
20+
2021
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2122
"github.com/hashicorp/terraform-plugin-sdk/v2/internal/addrs"
2223
"github.com/hashicorp/terraform-plugin-sdk/v2/internal/logging"
@@ -49,7 +50,7 @@ var flagSweepAllowFailures = flag.Bool("sweep-allow-failures", false, "Enable to
4950
var flagSweepRun = flag.String("sweep-run", "", "Comma seperated list of Sweeper Tests to run")
5051
var sweeperFuncs map[string]*Sweeper
5152

52-
// type SweeperFunc is a signature for a function that acts as a sweeper. It
53+
// SweeperFunc is a signature for a function that acts as a sweeper. It
5354
// accepts a string for the region that the sweeper is to be ran in. This
5455
// function must be able to construct a valid client for that region.
5556
type SweeperFunc func(r string) error
@@ -959,6 +960,68 @@ func testCheckResourceAttr(is *terraform.InstanceState, name string, key string,
959960
return nil
960961
}
961962

963+
// CheckResourceAttrWithFunc is the callback type used to apply a custom checking logic
964+
// when using TestCheckResourceAttrWith and a value is found for the given name and key.
965+
//
966+
// When this function returns an error, TestCheckResourceAttrWith will fail the check.
967+
type CheckResourceAttrWithFunc func(value string) error
968+
969+
// TestCheckResourceAttrWith ensures a value stored in state for the
970+
// given name and key combination, is checked against a custom logic.
971+
// State value checking is only recommended for testing Computed attributes
972+
// and attribute defaults.
973+
//
974+
// For managed resources, the name parameter is combination of the resource
975+
// type, a period (.), and the name label. The name for the below example
976+
// configuration would be "myprovider_thing.example".
977+
//
978+
// resource "myprovider_thing" "example" { ... }
979+
//
980+
// For data sources, the name parameter is a combination of the keyword "data",
981+
// a period (.), the data source type, a period (.), and the name label. The
982+
// name for the below example configuration would be
983+
// "data.myprovider_thing.example".
984+
//
985+
// data "myprovider_thing" "example" { ... }
986+
//
987+
// The key parameter is an attribute path in Terraform CLI 0.11 and earlier
988+
// "flatmap" syntax. Keys start with the attribute name of a top-level
989+
// attribute. Use the following special key syntax to inspect list, map, and
990+
// set attributes:
991+
//
992+
// - .{NUMBER}: List value at index, e.g. .0 to inspect the first element.
993+
// Use the TestCheckTypeSet* and TestMatchTypeSet* functions instead
994+
// for sets.
995+
// - .{KEY}: Map value at key, e.g. .example to inspect the example key
996+
// value.
997+
// - .#: Number of elements in list or set.
998+
// - .%: Number of elements in map.
999+
//
1000+
// The checkValueFunc parameter is a CheckResourceAttrWithFunc,
1001+
// and it's provided with the attribute value to apply a custom checking logic,
1002+
// if it was found in the state. The function must return an error for the
1003+
// check to fail, or `nil` to succeed.
1004+
func TestCheckResourceAttrWith(name, key string, checkValueFunc CheckResourceAttrWithFunc) TestCheckFunc {
1005+
return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error {
1006+
is, err := primaryInstanceState(s, name)
1007+
if err != nil {
1008+
return err
1009+
}
1010+
1011+
err = testCheckResourceAttrSet(is, name, key)
1012+
if err != nil {
1013+
return err
1014+
}
1015+
1016+
err = checkValueFunc(is.Attributes[key])
1017+
if err != nil {
1018+
return fmt.Errorf("%s: Attribute %q value: %w", name, key, err)
1019+
}
1020+
1021+
return nil
1022+
})
1023+
}
1024+
9621025
// TestCheckNoResourceAttr ensures no value exists in the state for the
9631026
// given name and key combination. The opposite of this TestCheckFunc is
9641027
// TestCheckResourceAttrSet. State value checking is only recommended for

helper/resource/testing_example_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package resource_test
22

33
import (
4+
"fmt"
45
"regexp"
6+
"strconv"
57

68
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
79
)
@@ -209,6 +211,67 @@ func ExampleTestCheckResourceAttr_typeString() {
209211
resource.TestCheckResourceAttr("example_thing.test", "example_string_attribute", "test-value")
210212
}
211213

214+
func ExampleTestCheckResourceAttrWith_typeString() {
215+
// This function is typically implemented in a TestStep type Check field,
216+
// wrapped with ComposeAggregateTestCheckFunc to combine results from
217+
// multiple checks.
218+
//
219+
// Given the following example configuration:
220+
//
221+
// resource "example_thing" "test" {
222+
// example_string_attribute = "Very long string..."
223+
// }
224+
//
225+
// The following TestCheckResourceAttrWith can be written to assert against
226+
// the expected state values.
227+
//
228+
// NOTE: State value checking is only necessary for Computed attributes,
229+
// as the testing framework will automatically return test failures
230+
// for configured attributes that mismatch the saved state, however
231+
// this configuration and test is shown for illustrative purposes.
232+
233+
// Verify the attribute value string length is above 1000
234+
resource.TestCheckResourceAttrWith("example_thing.test", "example_string_attribute", func(value string) error {
235+
if len(value) <= 1000 {
236+
return fmt.Errorf("should be longer than 1000 characters")
237+
}
238+
return nil
239+
})
240+
}
241+
242+
func ExampleTestCheckResourceAttrWith_typeInt() {
243+
// This function is typically implemented in a TestStep type Check field,
244+
// wrapped with ComposeAggregateTestCheckFunc to combine results from
245+
// multiple checks.
246+
//
247+
// Given the following example configuration:
248+
//
249+
// resource "example_thing" "test" {
250+
// example_int_attribute = 10
251+
// }
252+
//
253+
// The following TestCheckResourceAttrWith can be written to assert against
254+
// the expected state values.
255+
//
256+
// NOTE: State value checking is only necessary for Computed attributes,
257+
// as the testing framework will automatically return test failures
258+
// for configured attributes that mismatch the saved state, however
259+
// this configuration and test is shown for illustrative purposes.
260+
261+
// Verify the attribute value is an integer, and it's between 5 (included) and 20 (excluded)
262+
resource.TestCheckResourceAttrWith("example_thing.test", "example_string_attribute", func(value string) error {
263+
valueInt, err := strconv.Atoi(value)
264+
if err != nil {
265+
return err
266+
}
267+
268+
if valueInt < 5 && valueInt >= 20 {
269+
return fmt.Errorf("should be between 5 and 20")
270+
}
271+
return nil
272+
})
273+
}
274+
212275
func ExampleTestCheckResourceAttrPair() {
213276
// This function is typically implemented in a TestStep type Check field,
214277
// wrapped with ComposeAggregateTestCheckFunc to combine results from

0 commit comments

Comments
 (0)