Now I show you how to write a Test. We take the example of a function which extracts data out of a json file. This can be used for S3 Lambda event handling. You may either do the steps and learn someting or be lazy and clone the repository
git clone https://github.com/megaproaktiv/go-on-aws-source.git
cd go-on-aws-source/testing-go/gotest/eventutils
go test
The overall steps for creating automated tests:
go mod init eventutils
vi eventutils.go
package eventutils
import (
"github.com/aws/aws-lambda-go/events"
)
// ExtractKey simple extract fkt
func ExtractKey(s3event events.S3Event) string {
return ""
}
1 package eventutils
2
3 import (
4 "github.com/aws/aws-lambda-go/events"
5 )
6
7 // ExtractKey simple extract fkt
8 func ExtractKey(s3event events.S3Event) string{
9 return ""
10 }
Note that in line 9 we return an empty string. This function is bound to FAIL.
For the test itself you always need two imports:
import (
"testing"
"gotest.tools/assert"
)
testing
- for the testing framework"gotest.tools/assert"
for the assertion framework.Some people prefer "github.com/stretchr/testify/assert"
. The differences appear on a deeper level when you do deep comparison of structures. For now you could go with both, I prefer the gotest.tools.
Now we let VSCode help us:
This creates the file eventutils_test.go with some test stub code. We delete the function body and get:
package eventutils
import (
"testing"
"github.com/aws/aws-lambda-go/events"
)
func TestExtractKey(t *testing.T) {
}
1 package eventutils
2
3 import (
4 "testing"
5
6 "github.com/aws/aws-lambda-go/events"
7 )
8
9 func TestExtractKey(t *testing.T) {
10
11 }
Now we have the correct Package and the correct Function name.
For the test itself we import these two packages:
// Imports for automated testing
"testing"
"gotest.tools/assert"
For the preparation of the test data we need some more imports:
// Prepare Testdata
"encoding/json"
"io/ioutil"
"os"
"fmt"
Because we want to handle event data, I take the AWS S3 Put event (see source for complete data), which looks like this:
{
"Records": [
{
"s3": {
"object": {
"key": "object-key-demo3",
"size": 1024,
"eTag": "0123456789abcdef0123456789abcdef",
"sequencer": "0A1B2C3D4E5F678901"
}
}
}
]
}
We store the event file put.json
into the directory testdata
.
For the test, the input of the tested function is controlled testdata. We read the event file, transform it into a events.S3Even and call the function ExtractKey with it. Then we test whether the return value is object-key-demo3
, because that is the Key in the put.json event file.
func ExtractKey(s3event events.S3Event) string
14 func TestExtractKey(t *testing.T){
15 var s3event events.S3Event;
16
17 const testfile = "testdata/put.json"
18
19 jsonFile, err := os.Open(testfile)
20 if err != nil {
21 fmt.Println(err)
22 panic(err)
23 }
24 fmt.Println("Successfully Opened ", testfile)
25 defer jsonFile.Close()
26
27 byteValue, _ := ioutil.ReadAll(jsonFile)
28 if err != nil {
29 print(err)
30 }
31
32 err = json.Unmarshal([]byte(byteValue), &s3event)
line | explanation |
---|---|
15 | define event struct |
17-30 | read file with error check |
32 | transform (Unmarshal) the json file into the events.S3Event struct |
For the test we use the information from AWS about the S3 event and we import the eventutils package:
// The function to be tested
"eventutils"
"github.com/aws/aws-lambda-go/events"
Now the test itself:
37 expectedKey := "object-key-demo3"
38 realKey := eventutils.ExtractKey(s3event);
39 assert.Equal(t, expectedKey,realKey)
40
As we inspect the put.json, we know what the extracted key should be (see line 37).
Then, in line 38 we actually call the tested function.
At the end we assert that the expectedKey is equals the realKey.
See the whole testfile on github
Call test with:
go test
Output:
Successfully Opened testdata/put.json
--- FAIL: TestExtractKey (0.00s)
eventutils_test.go:39: assertion failed: object-key-demo3 (expectedKey string) != (realKey string)
FAIL
exit status 1
FAIL eventutils 0.291s
Hurray, it fails - this should be the case. With Test Driven Development the first test should fail. This way you make sure that you are really testing something and don`t get a false positive.
Now you change eventutils.go to the real code.
1 package eventutils
2
3 import (
4 "github.com/aws/aws-lambda-go/events"
5 )
6
7 // ExtractKey simple extract fkt
8 func ExtractKey(s3event events.S3Event) string{
9 return s3event.Records[0].S3.Object.Key;
10 }
go test
You get:
Successfully Opened testdata/put.json
PASS
ok eventutils 0.240s
Now were done!
Yes, for the first time it is. The second time you will re-use the “read a json file and create an AWS structure” and have a lot less code to write.
You trade some time investment at the beginning for a better code quality at the end.
Working with events, a simple typo could give errors which are not easy to find. So if you check the quality of the code, here the correct results of the function, the better the quality will be.
Manually testing the function is much more expensive. The execution time of the test locally is under one second:
time go test
Successfully Opened testdata/put.json
PASS
ok eventutils 0.245s
go test 0,53s user 0,88s system 180% cpu 0,781 total
When you perform the same test manually in the AWS Console, you need at least a minute with code update, deploy, click, etc..
You run all test each time you change something in your code. So you will create a safety net for the correct code results.
See the full source on github.