Writing a Test

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:

graph LR; S((START)) --> A A(1 function skeleton) --> B(2 write test code) B --> D{3 Test} D -->|PASS| E(END) D -->|FAIL| C(4 write code) C --> D

1 Write Function skeleton

  1. Create module
go mod init eventutils
  1. Create file
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.

2 Write Test Code

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"

Prepare Test Data

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

Do the Test

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

3 Test

Call test with:

go test

FAIL

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.

4 Write code

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 }

3 Test again

go test

PASS

You get:

Successfully Opened  testdata/put.json
PASS
ok  	eventutils	0.240s

Now were done!

Some more things to consider

This is a lot of work for some simple testing

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.

This function is trivial, why should I test it?

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..

How often do i perform the test?

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 also

Source

See the full source on github.

Sources