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