Validation Tests

Validation Tests #

Sometimes we want the inputs to be configurable, but we also want to put constraints on those inputs or validate that the input is valid.

Suppose for the HitCounter construct we want to allow the user to specify the ReadCapacity on the DynamoDB table, but we also want to ensure the value is within a reasonable range. We can write a test to make sure that the validation logic works: pass in invalid values and see what happens.

First, add a ReadCapacity property to the HitCounterProps struct:

type HitCounterProps struct {
  // Downstream is the function for which we want to count hits
	Downstream   awslambda.IFunction
	ReadCapacity float64
}

Then update the DynamoDB table resource to add the ReadCapacity property.

	table := awsdynamodb.NewTable(this, jsii.String("Hits"), &awsdynamodb.TableProps{
		PartitionKey:  &awsdynamodb.Attribute{Name: jsii.String("path"), Type: awsdynamodb.AttributeType_STRING},
		RemovalPolicy: awscdk.RemovalPolicy_DESTROY,
		Encryption:    awsdynamodb.TableEncryption_AWS_MANAGED,
		ReadCapacity:  &props.ReadCapacity,
	})

Now add a validation which will throw an error if the ReadCapacity is not in the allowed range.

func NewHitCounter(scope constructs.Construct, id string, props *HitCounterProps) HitCounter {
	if props.ReadCapacity < 5 || props.ReadCapacity > 20 {
		panic("ReadCapacity must be between 5 and 20")
	}

Don’t forget to pass in the ReadCapacity both in our app where we create a HitCounter, and in our existing tests as well

hitcounter := hitcounter.NewHitCounter(stack, "HelloHitCounter", &hitcounter.HitCounterProps{
	Downstream:   helloHandler,
	ReadCapacity: 10,
})

Now lets add a test that validates the error is thrown.

func TestCanPassReadCapacity(t *testing.T) {
  defer jsii.Close()
  defer func() {
    if r := recover(); r == nil {
      t.Error("Did not throw ReadCapacity error")
    }
  }()

  // GIVEN
  stack := awscdk.NewStack(nil, nil, nil)

  // WHEN
  testFn := awslambda.NewFunction(stack, jsii.String("TestFunction"), &awslambda.FunctionProps{
    Code: awslambda.Code_FromAsset(jsii.String("lambda"), nil),
    Runtime: awslambda.Runtime_NODEJS_16_X(),
    Handler: jsii.String("hello.handler"),
  })
  hitcounter.NewHitCounter(stack, "MyTestConstruct", &hitcounter.HitCounterProps{
    Downstream: testFn,
    ReadCapacity: 10,
  })
}

Run the test.

$ go test

You should see an output like this:

╰─ go test
--- FAIL: TestCanPassReadCapacity (0.01s)
    cdk-workshop_test.go:78: Did not throw ReadCapacity error
FAIL
exit status 1
FAIL    cdk-workshop    5.442s

Now let’s change the value of ReadCapacity to be outside the valid range so that this test can succeed

func TestCanPassReadCapacity(t *testing.T) {
  defer func() {
    if r := recover(); r == nil {
      t.Error("Did not throw ReadCapacity error")
    }
  }()

  // GIVEN
  stack := awscdk.NewStack(nil, nil, nil)

  // WHEN
  testFn := awslambda.NewFunction(stack, jsii.String("TestFunction"), &awslambda.FunctionProps{
    Code: awslambda.Code_FromAsset(jsii.String("lambda"), nil),
    Runtime: awslambda.Runtime_NODEJS_16_X(),
    Handler: jsii.String("hello.handler"),
  })
  hitcounter.NewHitCounter(stack, "MyTestConstruct", &hitcounter.HitCounterProps{
    Downstream: testFn,
    ReadCapacity: 21,
  })
}

Now when we run the test it should succeed

╰─ go test
PASS
ok      cdk-workshop    5.384s

We use analytics to make this content better, but only with your permission.

More information