Now we can invoke the lambda itself with a self generated event. This is the integration test.
When we start the Test with the event from “outside”, that means putting an object in the bucket, we test the whole application end2end. And because this end2end Test uses the *application which runs on the infrastructure, there is only one end2end test scenario per usecase, not two.
I have not coded this example in go, maybe you try to automate it yourself.
The steps are shown in the Taskfile.yml. So when you call task itest
, all steps are shown.
You can try it on the AWS CLI with:
aws lambda invoke --function-name {{.FN}} --payload fileb://./test/put.json test/result.json
Where FN is the function name.
Or check the wholy cycle with the cloned go-on-aws-source repository:
cd architectures/serverless/app
task itest
Gives the output:
task: [itest] aws dynamodb delete-item --table-name dsl-items07D08F4B-S4BK34JCHWDO --key file://testdata/key.json
task: [itest] aws dynamodb get-item --table-name dsl-items07D08F4B-S4BK34JCHWDO --key file://testdata/key.json
task: [itest] time aws lambda invoke --function-name logincomingobject --payload fileb://./test/put.json test/result.json
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
real 0m1.037s
user 0m0.000s
sys 0m0.000s
task: [itest] date
Sa 18 Dez 2021 16:20:26 CET
task: [itest] sleep 5
task: [itest] aws dynamodb get-item --table-name dsl-items07D08F4B-S4BK34JCHWDO --key file://testdata/key.json
{
"Item": {
"itemID": {
"S": "my2etestkey.txt"
},
"time": {
"S": "2021-12-18 15:20:26.630121344 +0000 UTC m=+0.052973681"
}
}
}
You could use LambCI or AWS SAM to invoke lambda on you laptop on a docker container. The arguments to do that are usually, that debugging with single steps are easier that way.
I prefer to debug unit test, which also gives you the debugging possibility and to invoke physical Lambda functions and some more:
Here you have to do a clean setup and teardown of the database, so the steps are:
The steps are coded in architectures/serverless/app/integration_test.go in the test TestAppInvokeLambdaWithEvent.
58 var itemID = "my2etestkey.txt"
59
60 key := map[string]types.AttributeValue{
61 "itemID": &types.AttributeValueMemberS{Value: itemID},
62 }
...
66 parmsDDBDelete := &dynamodb.DeleteItemInput{
67 Key: key,
68 TableName: &table,
69 }
70 t.Log("Setup - delete item")
71 _, err := ClientD.DeleteItem(context.TODO(), parmsDDBDelete)
72 assert.NilError(t, err, "Delete Item should work")
In lines 60-62 you see how to encode a DynamoDB String attribute AttributeValueMemberS.
73 parmsDDBGet := &dynamodb.GetItemInput{
74 Key: key,
75 TableName: &table,
76 ConsistentRead: aws.Bool(true),
77 }
78 responseDDB, err := ClientD.GetItem(context.TODO(), parmsDDBGet)
79 assert.Equal(t, 0, len(responseDDB.Item), "Delete should work")
In line 79 I test that before the test the database is clean. This is the Setup of the test.
81 // *** Copy object to S3
82 testObjectFilename := "./testdata/dummy.txt"
83 file, err := os.Open(testObjectFilename)
84 assert.NilError(t, err, "Open file "+testObjectFilename+" should work")
85
86 defer file.Close()
87 t.Log("Copy object to S3")
88 parmsS3 := &s3.PutObjectInput{
89 Bucket: &bucket,
90 Key: &itemID,
91 Body: file,
92 }
93 _, err = ClientS.PutObject(context.TODO(), parmsS3)
94 assert.NilError(t, err, "Put Object "+itemID+" should work")
This is quite straighforward. Just call the S3 PutObject API.
96 t.Log("Sleep 3 seconds")
97 time.Sleep(3 * time.Second)
With this event based system you could argue, that waiting is not the right method. But with the test you don’t want to change the tested system. So if you would change the DynamoDB Table and add an trigger, you would change the tested system. When we change the tested system, this could have unwanted side-effects.
99 t.Log("Test item on DynamoDB")
100 responseDDB, err = ClientD.GetItem(context.TODO(), parmsDDBGet)
101 assert.NilError(t, err, "Get Item should work")
102 // Item itsef with attribute time
103 assert.Equal(t, 2, len(responseDDB.Item))
Now this is the real end2end test. The system behaves as defined. Yes! Go celebrate.
export I_TEST="yes"
go test -run TestAppInvokeLambdaWithEvent -v
Should give:
=== RUN TestAppInvokeLambdaWithEvent
integration_test.go:70: Setup - delete item
integration_test.go:87: Copy object to S3
integration_test.go:96: Sleep 3 seconds
integration_test.go:99: Test item on DynamoDB
--- PASS: TestAppInvokeLambdaWithEvent (3.29s)
PASS
ok dsl 3.706s
Usually you clean up after the test. But in the early phaes you want to have a look at the generated item, so no cleanup yet.
With all these test types you have a safety net for you app. So if you want to refactor something, all functionality is secured!
See the full source on github.