为了账号安全,请及时绑定邮箱和手机立即绑定

AWS SDK Go Lambda 单元测试

AWS SDK Go Lambda 单元测试

Go
桃花长相依 2022-07-25 10:47:32
我第一次尝试全神贯注地编写一些单元测试,并且我正在 golang 中为使用 aws lambda 的辅助项目做这件事。下面是两个文件。main.go接受一个包含电子邮件地址的事件,并在认知用户池中创建用户。main_test.go应该模拟中的createUser函数main.go,但是当我尝试运行测试时出现错误。观看此 youtube 视频后,我刚刚将我的代码从全局实例化客户端切换为在 aws sdk 接口上使用指针接收器方法。错误Running tool: /usr/local/go/bin/go test -timeout 30s -run ^TestCreateUser$ github.com/sean/repo/src/create_user--- FAIL: TestCreateUser (0.00s)    --- FAIL: TestCreateUser/Successfully_create_user (0.00s)panic: runtime error: invalid memory address or nil pointer dereference [recovered]    panic: runtime error: invalid memory address or nil pointer dereference[signal SIGSEGV: segmentation violation code=0x1 addr=0x60 pc=0x137c2f2]goroutine 6 [running]:testing.tRunner.func1.1(0x13e00c0, 0x173fd70)    /usr/local/go/src/testing/testing.go:1072 +0x30dtesting.tRunner.func1(0xc000001b00)    /usr/local/go/src/testing/testing.go:1075 +0x41apanic(0x13e00c0, 0x173fd70)    /usr/local/go/src/runtime/panic.go:969 +0x1b9github.com/sean/repo/src/create_user.(*mockCreateUser).AdminCreateUser(0xc00000ee40, 0xc00006a200, 0xc00001e39d, 0x18, 0xc0000b24b0)    <autogenerated>:1 +0x32github.com/sean/repo/src/create_user.(*awsService).createUser(0xc000030738, 0x145bbca, 0x10, 0x18, 0x0)    /Users/sean/code/github/sean/repo/src/create_user/main.go:39 +0x2b7github.com/sean/repo/src/create_user.TestCreateUser.func1(0xc000001b00)    /Users/sean/code/github/sean/repo/src/create_user/main_test.go:30 +0x10ctesting.tRunner(0xc000001b00, 0x1476718)    /usr/local/go/src/testing/testing.go:1123 +0xefcreated by testing.(*T).Run    /usr/local/go/src/testing/testing.go:1168 +0x2b3FAIL    github.com/sean/repo/src/create_user    0.543sFAIL
查看完整描述

1 回答

?
白衣非少年

TA贡献1155条经验 获得超0个赞

我认为这里的问题是,您想模拟该AdminCreateUser()方法,但实际上确实模拟了该CreateUser()方法。


因此,当您创建mockCreateUser结构的新实例时,“实现”cidpif.CognitoIdentityProviderAPI接口,然后调用它的AdminCreateUser()方法,它没有实现并且失败。


你的相关代码main_test.go应该是这样的:


type mockCreateUser struct {

    cidpif.CognitoIdentityProviderAPI

    Response cidp.AdminCreateUserOutput

}


func (d mockCreateUser) CreateUser(e createUserEvent) error {

    return nil

}

CreateUser()添加以下“虚拟”(并删除该方法)就足够了:


func (d mockCreateUser) AdminCreateUser(*cidp.AdminCreateUserInput) (*cidp.AdminCreateUserOutput, error) {

    return d.Response, nil

}

此外,我想提出一种稍微不同的方法来对您的 Lambda 进行单元测试。你的代码在可测试性方面已经相当不错了。但你可以做得更好。


我建议创建一个与您的awsService结构类似的“应用程序”,但不实现任何 AWS 接口。相反,它包含一个configuration结构。此配置包含您从环境中读取的值(例如USER_POOL_ID, EMAIL)以及 AWS 服务的实例。


这个想法是您的所有方法和函数都使用此配置,允许您在单元测试期间使用模拟 AWS 服务并在运行时使用“适当的”服务实例。


以下是您的 Lambda 的简化版本。显然,命名等取决于您。还有很多错误处理缺失等。


我认为最大的优势是,您可以config通过application. 如果您想在每个测试和不同的行为中使用不同的电子邮件等,您只需更改配置即可。


main.go


package main


import (

    "os"


    "github.com/aws/aws-lambda-go/events"

    "github.com/aws/aws-lambda-go/lambda"

    "github.com/aws/aws-sdk-go/aws"

    "github.com/aws/aws-sdk-go/aws/session"

    "github.com/aws/aws-sdk-go/service/cognitoidentityprovider"

    "github.com/aws/aws-sdk-go/service/cognitoidentityprovider/cognitoidentityprovideriface"

)


type createUserEvent struct {

    EmailAddress string `json:"email_address"`

}


type configuration struct {

    poolId string

    idp    cognitoidentityprovideriface.CognitoIdentityProviderAPI

}


type application struct {

    config configuration

}


func (app *application) createUser(event createUserEvent) error {

    input := &cognitoidentityprovider.AdminCreateUserInput{

        UserPoolId:             aws.String(app.config.poolId),

        Username:               aws.String(event.EmailAddress),

        DesiredDeliveryMediums: aws.StringSlice([]string{"EMAIL"}),

        ForceAliasCreation:     aws.Bool(true),

        UserAttributes: []*cognitoidentityprovider.AttributeType{

            {

                Name:  aws.String("email"),

                Value: aws.String(event.EmailAddress),

            },

        },

    }


    _, err := app.config.idp.AdminCreateUser(input)

    if err != nil {

        return err

    }


    return nil

}


func (app *application) handler(event createUserEvent) (events.APIGatewayProxyResponse, error) {

    err := app.createUser(event)

    if err != nil {

        return events.APIGatewayProxyResponse{}, err

    }


    return events.APIGatewayProxyResponse{}, nil

}


func main() {

    config := configuration{

        poolId: os.Getenv("USER_POOL_ID"),

        idp:    cognitoidentityprovider.New(session.Must(session.NewSession())),

    }


    app := application{config: config}


    lambda.Start(app.handler)

}

main_test.go


package main


import (

    "testing"


    "github.com/aws/aws-sdk-go/service/cognitoidentityprovider"

    "github.com/aws/aws-sdk-go/service/cognitoidentityprovider/cognitoidentityprovideriface"

)


type mockAdminCreateUser struct {

    cognitoidentityprovideriface.CognitoIdentityProviderAPI

    Response *cognitoidentityprovider.AdminCreateUserOutput

    Error    error

}


func (d mockAdminCreateUser) AdminCreateUser(*cognitoidentityprovider.AdminCreateUserInput) (*cognitoidentityprovider.AdminCreateUserOutput, error) {

    return d.Response, d.Error

}


func TestCreateUser(t *testing.T) {

    t.Run("Successfully create user", func(t *testing.T) {

        idpMock := mockAdminCreateUser{

            Response: &cognitoidentityprovider.AdminCreateUserOutput{},

            Error:    nil,

        }


        app := application{config: configuration{

            poolId: "test",

            idp:    idpMock,

        }}


        err := app.createUser(createUserEvent{EmailAddress: "user@example.com"})

        if err != nil {

            t.Fatal("User should have been created")

        }

    })

}


查看完整回答
反对 回复 2022-07-25
  • 1 回答
  • 0 关注
  • 100 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信