Ich bin vor kurzem zu Golang als Sprache meiner Wahl gewechselt. (In meinem vorherigen Blog können Sie lesen, warum.) Aber ich bin auch ein großer Fan der testgetriebenen Entwicklung. Mit Python haben Sie einen Stubber, mit dem Sie die AWS-API nachbilden können. Wie machen Sie das also in Golang? In diesem Blog werde ich meine bisherigen Erfahrungen mit Ihnen teilen.
Verwenden Sie die Injektion von Abhängigkeiten
Mein erstes Experiment war die Injektion von Abhängigkeiten. Dazu habe ich den folgenden Code verwendet:
package main
import (
"context"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
"time"
"log"
"os"
)
type Request struct {}
type Response struct {}
type Lambda struct {
s3Client *s3.Client
}
func New() (*Lambda, error) {
cfg, err := config.LoadDefaultConfig(context.TODO())
m := new(Lambda)
m.SetS3Client(s3.NewFromConfig(cfg))
return m, err
}
func (x *Lambda) SetS3Client(client *s3.Client) {
x.s3Client = client
}
func (x *Lambda) Handler(ctx context.Context, request Request) (Response, error) {
// Your lambda code goes here
}
In Ihren Tests können Sie es nun wie folgt verwenden:
package main
import (
"context"
"github.com/aws/aws-sdk-go-v2/service/s3"
"testing"
"time"
"log"
"os"
)
type mockS3Client struct {
s3.Client
Error error
}
func (m *mockS3Client) PutObject(input *s3.PutObjectInput) (*s3.PutObjectOutput, error) {
return &s3.PutObjectOutput{}, nil
}
func TestHandler(t *testing.T) {
lambda := New()
lambda.SetS3Client(&mockS3Client{})
var ctx = context.Background()
var event Request
t.Run("Invoke Handler", func(t *testing.T) {
response, err := lambda.Handler(ctx, event)
// Perform Assertions
})
}
Wir injizieren ein gespottetes Objekt, das als Client fungiert, um die API-Aufrufe auszuführen. Mit diesem Ansatz konnte ich nun einige Tests schreiben. Aber mir wurde klar, dass dieser Ansatz ein weiteres Problem mit sich bringt. Was ist zum Beispiel, wenn Sie 2 API-Aufrufe haben, die einen PutObject -Aufruf ausführen. In diesem Beispiel gebe ich ein leeres
Verwendung eines Stubbers
Also habe ich weiter recherchiert und das awsdocs/aws-doc-sdk-examples Repository gefunden. Dieses Repository verwendet ein testtools Modul. Also habe ich ein Experiment gestartet, um zu sehen, wie ich dieses Modul verwenden kann. Ich habe den Code wie folgt umstrukturiert:
package main
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
type Request struct {}
type Response struct {}
type Lambda struct {
ctx context.Context
s3Client *s3.Client
}
func New(cfg aws.Config) *Lambda {
m := new(Lambda)
m.s3Client = s3.NewFromConfig(cfg)
return m
}
func (x *Lambda) Handler(ctx context.Context, request Request) (Response, error) {
// Your lambda code goes here
return Response{}, nil
}
Ich habe der Methode New einen Parameter cfg hinzugefügt, den ich also auch in meiner Hauptmethode übergeben muss.
package main
import (
"context"
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go-v2/config"
"log"
)
func main() {
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
log.Printf("error: %v", err)
return
}
lambda.Start(New(cfg).Handler)
}
Der Test selbst sieht folgendermaßen aus:
package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/awsdocs/aws-doc-sdk-examples/gov2/testtools"
"io"
"os"
"strings"
"testing"
)
func TestHandler(t *testing.T) {
var ctx = context.Background()
var event Request
t.Run("Upload a file to S3", func(t *testing.T) {
stubber := testtools.NewStubber()
lambda := New(*stubber.SdkConfig)
stubber.Add(testtools.Stub{
OperationName: "PutObject",
Input: &s3.PutObjectInput{
Bucket: aws.String("my-sample-bucket"),
Key: aws.String("my/object.json"),
Body: bytes.NewReader([]byte{}),
},
Output: &s3.PutObjectOutput{},
})
response, err := lambda.Handler(ctx, event)
testtools.ExitTest(stubber, t)
// Perform Assertions
})
}
Wie Sie sehen können, haben wir den Mock jetzt in den Test selbst verschoben. Dadurch können Sie die AWS-API auf der Grundlage Ihres Tests reagieren lassen. Der größte Vorteil ist, dass es im Test selbst gekapselt ist. Wenn Sie zum Beispiel ein Szenario hinzufügen möchten, bei dem der Aufruf von PutObject fehlschlägt, fügen Sie Folgendes hinzu:
t.Run("Fail on upload", func(t *testing.T) {
stubber := testtools.NewStubber()
lambda := New(*stubber.SdkConfig)
raiseErr := &testtools.StubError{Err: errors.New("ClientError")}
stubber.Add(testtools.Stub{
OperationName: "PutObject",
Input: &s3.PutObjectInput{
Bucket: aws.String("my-sample-bucket"),
Key: aws.String("my/object.json"),
Body: bytes.NewReader([]byte{}),
},
Error: raiseErr,
})
_, err := lambda.Handler(ctx, event)
testtools.VerifyError(err, raiseErr, t)
testtools.ExitTest(stubber, t)
})
Die Definition testtools.VerifyError(err, raiseErr, t) bestätigt, ob der Fehler tatsächlich weitergegeben wird. Die Definition
In manchen Fällen möchten Sie bestimmte Felder in Ihrem Input ignorieren. Sie können eine Liste von
Fazit
Die testtool ist ein guter Ersatz für den Stubber, den ich in Python verwendet habe. Er ermöglicht es Ihnen, Szenariodaten in Ihrem Test zu kapseln. Damit vermeiden Sie schwer zu pflegende Mock-Objekte. Die
Foto von Klaus Nielsen
Verfasst von

Joris Conijn
Joris is the AWS Practise CTO of the Xebia Cloud service line and has been working with the AWS cloud since 2009 and focussing on building event-driven architectures. While working with the cloud from (almost) the start, he has seen most of the services being launched. Joris strongly believes in automation and infrastructure as code and is open to learning new things and experimenting with them because that is the way to learn and grow.
Unsere Ideen
Weitere Blogs
Contact




