如何编写单元测试 aws-sdk-go-v2 dynamodb 实现

How do I write unit test aws-sdk-go-v2 dynamodb implementation

我还在掌握 go-interfaces,我可以模拟 WaitUntilTableExists func. But unable to mock PutItemRequest

这是我的 main.go 片段

func MyPutItem(d mydata, client dynamodbiface.DynamoDBAPI) error {
    input := &dynamodb.PutItemInput{
        ....
    }
    req := client.PutItemRequest(input)
    result, err := req.Send()
    log.Println(result)
    return err
}

main_test.go 片段

type mockDynamoDBClient struct {
    dynamodbiface.DynamoDBAPI
}

func (m *mockDynamoDBClient) PutItemRequest(input *dynamodb.PutItemInput) dynamodb.PutItemRequest {
    // Most probably this is where I need your help
}

func TestStoreInDynamoDB(t *testing.T) {
    var mockClient = new(mockDynamoDBClient)
    d := mydata{}
    result := DynampDBPutItem(d, mockClient)
    t.Log(result)
}

以您的示例为例,您可以直接在模拟中进行断言

type mockDynamoDBClient struct {
    t *testing.T
    expected *dynamodb.PutItemInput
    response *dynamodb.PutItemOutput
    dynamodbiface.DynamoDBAPI
}

func (m *mockDynamoDBClient) PutItemRequest(input *dynamodb.PutItemInput) dynamodb.PutItemOutput {
    // some kind of equality check
    if !reflect.DeepEqual(m.expected, input) {
        t.Errorf(...// some error message)
    }
    return m.response
}

这个例子的主要问题是:

t *testing.Texpected *dynamodb.PutItemInput 和响应 response *dynamodb.PutItemOutput 都需要在感觉凌乱的结构中。

相反,您可以使用匿名函数来执行此操作:

type mockDynamoDBClient struct {
    f func(input *dynmaodb.PutItemInput) *dynamodb.PutItemOutput
    dynamodbiface.DynamoDBAPI
}

func (m *mockDynamoDBClient) PutItemRequest(input *dynamodb.PutItemInput) dynamodb.PutItemOutput {
    return m.f(input)
}

现在在测试代码中你可以稍微更好地使用模拟结构:

m := &mockDynamoDBClient{
    f: func(input *dynamodb.PutItemInput) *dynamodb.PutItemOutput {
        // assertions on input
        // return mock responses
    }
}

编辑 基于评论:

您还应该考虑让您的 MyPutItem 函数依赖尽可能小的接口。如果您只需要访问 PutItemRequest 方法,那么您可以为该方法创建自己的接口并在 MyPutItem

中使用它
type MyDynamoPutter interface {
    func (c *DynamoDB) PutItemRequest(input *PutItemInput) PutItemRequest
}

然后在MyPutItem你可以使用你自己的界面:

func MyPutItem(d mydata, client MyDynamoPutter) error {
    input := &dynamodb.PutItemInput{
        ....
    }
    req := client.PutItemRequest(input)
    result, err := req.Send()
    log.Println(result)
    return err
}

这减少了您需要模拟的表面积!

像这样伪造 SDK 可行:

main_test.go

type fakeDynamoDBClient struct {
    dynamodbiface.DynamoDBAPI
}

func (m *fakeDynamoDBClient) GetItemRequest(input *dynamodb.GetItemInput) dynamodb.GetItemRequest {
    return dynamodb.GetItemRequest{
        Request: &aws.Request{
            Data: &dynamodb.GetItemOutput{
                Item: map[string]dynamodb.AttributeValue{
                    "count": dynamodb.AttributeValue{
                        N: aws.String("10"),
                    },
                },
            },
        },
    }
}

func (m *fakeDynamoDBClient) PutItemRequest(input *dynamodb.PutItemInput) dynamodb.PutItemRequest {
    return dynamodb.PutItemRequest{
        Request: &aws.Request{
            Data: &dynamodb.PutItemOutput{},
        },
    }
}

func TestUpdateCount(t *testing.T) {
    err := UpdateCount(10, &fakeDynamoDBClient{})
    if err != nil {
        t.Error("Failed to update badge count on dynamodb", err)
    }
}

main.go

func UpdateCount(count int, client dynamodbiface.DynamoDBAPI) error {
    ...
}