在测试Golang AWS Lambdas时,可以在模拟外部函数的同时重用代码。

huangapple go评论198阅读模式
英文:

Reuse code while mocking external funcs when testing golang aws lambdas

问题

我有一个使用AWS Lambda的Golang应用程序。我想为它编写单元测试。

这是测试代码:

  1. func TestHandleLambdaEvent(t *testing.T) {
  2. ctx := context.TODO()
  3. //mockNewAuthContextWithMap()
  4. oldNewAuthContextWithMap := NewAuthContextWithMap
  5. defer func() { NewAuthContextWithMap = oldNewAuthContextWithMap }()
  6. NewAuthContextWithMap = func(stringifiedMap map[string]interface{}) (*authutils.AuthContext, error) {
  7. return &authutils.AuthContext{UserID: "12345", Org: "XYZOrg", Role: "Member", Timestamp: 999999999}, nil
  8. }
  9. //mockLambdaInvoke()
  10. old := LambdaInvoke
  11. defer func() { LambdaInvoke = old }()
  12. LambdaInvoke = func(context context.Context, arn string, request, response interface{}) error { return nil }
  13. resp, err := handleLambdaEvent(ctx, events.APIGatewayProxyRequest{})
  14. if err != nil {
  15. t.Fatalf("handleLambdaEvent returned error: %v", err)
  16. }
  17. if resp.StatusCode != http.StatusOK {
  18. t.Fatalf("Invalid status code, provded: %d required %d", resp.StatusCode, http.StatusOK)
  19. }
  20. }

这段测试代码运行良好,但是我想将代码放在mockNewAuthContextWithMap函数中,然后从测试中调用它。但是这样不起作用。我猜是因为defer函数在测试调用处理程序之前运行。

我该如何修复它?看起来我可以重用这部分代码:

  1. NewAuthContextWithMap = func(stringifiedMap map[string]interface{}) (*authutils.AuthContext, error) {
  2. return &authutils.AuthContext{UserID: "12345", Org: "XYZOrg", Role: "Member", Timestamp: 999999999}, nil
  3. }

但是使用defer函数保存旧的函数值并回滚它必须重复进行。

编辑:

可能我唯一能做的就是:

  1. func TestHandleLambdaEvent(t *testing.T) {
  2. ctx := context.TODO()
  3. mockNewAuthContextWithMap()
  4. mockLambdaInvoke()
  5. resp, err := handleLambdaEvent(ctx, events.APIGatewayProxyRequest{})
  6. if err != nil {
  7. t.Fatalf("handleLambdaEvent returned error: %v", err)
  8. }
  9. if resp.StatusCode != http.StatusOK {
  10. t.Fatalf("Invalid status code, provded: %d required %d", resp.StatusCode, http.StatusOK)
  11. }
  12. defer RollbackExternalMethods()
  13. }
  14. // LambdaInvoke
  15. func mockNewAuthContextWithMap() {
  16. NewAuthContextWithMap = func(stringifiedMap map[string]interface{}) (*authutils.AuthContext, error) {
  17. return &authutils.AuthContext{UserID: "12345", Org: "XYZOrg", Role: "Member", Timestamp: 999999999}, nil
  18. }
  19. }
  20. // LambdaInvoke
  21. func mockLambdaInvoke() {
  22. LambdaInvoke = func(context context.Context, arn string, request, response interface{}) error { return nil }
  23. }
  24. func RollbackExternalMethods() {
  25. NewAuthContextWithMap = authutils.NewAuthContextWithMap
  26. LambdaInvoke = lambdaClient.Invoke
  27. }

编辑:handleLambdaEvent

  1. func handleLambdaEvent(context context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
  2. authContext, err := NewAuthContextWithMap(request.RequestContext.Authorizer)
  3. if err != nil {
  4. fmt.Println("Error parsing auth context:", err)
  5. return awsutils.StatusResponse(http.StatusInternalServerError), nil
  6. }
  7. queryRequest := handlerInput.GetProfile{
  8. Type: handlerInput.TypeGetProfile,
  9. UserId: authContext.UserID,
  10. }
  11. queryResp := dbModel.User{}
  12. err = LambdaInvoke(context, userServiceArn, queryRequest, &queryResp)
  13. if err != nil {
  14. if ierrError, ok := err.(ierr.Error); ok {
  15. if ierrError.IsSame(user.RecordNotFoundError) {
  16. fmt.Printf("Could not find user profile of userId: %s \n", authContext.UserID)
  17. emptyResp := dbModel.User{}
  18. return awsutils.SwaggerResponse(http.StatusOK, emptyResp.SwaggerModel()), nil
  19. }
  20. }
  21. fmt.Println("Error invoking lambda:", err)
  22. return awsutils.StatusResponse(http.StatusInternalServerError), nil
  23. }
  24. swagUser := queryResp.SwaggerModel()
  25. return awsutils.SwaggerResponse(http.StatusOK, swagUser), nil
  26. }
英文:

I have AWS lambdas in my golang app.
Wanted to write unit tests for it:

  1. func TestHandleLambdaEvent(t *testing.T) {
  2. ctx := context.TODO()
  3. //mockNewAuthContextWithMap()
  4. oldNewAuthContextWithMap := NewAuthContextWithMap
  5. defer func() { NewAuthContextWithMap = oldNewAuthContextWithMap }()
  6. NewAuthContextWithMap = func(stringifiedMap map[string]interface{}) (*authutils.AuthContext, error) {
  7. return &authutils.AuthContext{UserID: "12345", Org: "XYZOrg", Role: "Member", Timestamp: 999999999}, nil
  8. }
  9. //mockLambdaInvoke()
  10. old := LambdaInvoke
  11. defer func() { LambdaInvoke = old }()
  12. LambdaInvoke = func(context context.Context, arn string, request, response interface{}) error { return nil }
  13. resp, err := handleLambdaEvent(ctx, events.APIGatewayProxyRequest{})
  14. if err != nil {
  15. t.Fatalf("handleLambdaEvent returned error: %v", err)
  16. }
  17. if resp.StatusCode != http.StatusOK {
  18. t.Fatalf("Invalid status code, provded: %d required %d", resp.StatusCode, http.StatusOK)
  19. }
  20. }

this test code works fine, but.. I would like to put code under mockNewAuthContextWithMap to func ie:

  1. func mockNewAuthContextWithMap() {
  2. old := NewAuthContextWithMap
  3. defer func() { NewAuthContextWithMap = old }()
  4. NewAuthContextWithMap = func(stringifiedMap map[string]interface{}) (*authutils.AuthContext, error) {
  5. return &authutils.AuthContext{UserID: "12345", Org: "XYZOrg", Role: "Member", Timestamp: 999999999}, nil
  6. }
  7. }

and simply call it from the test. Then it does not work.
I assume its due to defer func, which simply run before test will call handler.

how can I fix it ?
looks like I can reuse this part of code:

  1. NewAuthContextWithMap = func(stringifiedMap map[string]interface{}) (*authutils.AuthContext, error) {
  2. return &authutils.AuthContext{UserID: "12345", Org: "XYZOrg", Role: "Member", Timestamp: 999999999}, nil
  3. }

but saving old function value with defer func that will rollback it must be repeated everytime.

EDIT:

probably only what I can do is:

  1. func TestHandleLambdaEvent(t *testing.T) {
  2. ctx := context.TODO()
  3. mockNewAuthContextWithMap()
  4. mockLambdaInvoke()
  5. resp, err := handleLambdaEvent(ctx, events.APIGatewayProxyRequest{})
  6. if err != nil {
  7. t.Fatalf("handleLambdaEvent returned error: %v", err)
  8. }
  9. if resp.StatusCode != http.StatusOK {
  10. t.Fatalf("Invalid status code, provded: %d required %d", resp.StatusCode, http.StatusOK)
  11. }
  12. defer RollbackExternalMethods()
  13. }
  14. // LambdaInvoke
  15. func mockNewAuthContextWithMap() {
  16. NewAuthContextWithMap = func(stringifiedMap map[string]interface{}) (*authutils.AuthContext, error) {
  17. return &authutils.AuthContext{UserID: "12345", Org: "XYZOrg", Role: "Member", Timestamp: 999999999}, nil
  18. }
  19. }
  20. // LambdaInvoke
  21. func mockLambdaInvoke() {
  22. LambdaInvoke = func(context context.Context, arn string, request, response interface{}) error { return nil }
  23. }
  24. func RollbackExternalMethods() {
  25. NewAuthContextWithMap = authutils.NewAuthContextWithMap
  26. LambdaInvoke = lambdaClient.Invoke
  27. }

EDIT: handleLambdaEvent

  1. func handleLambdaEvent(context context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
  2. authContext, err := NewAuthContextWithMap(request.RequestContext.Authorizer)
  3. if err != nil {
  4. fmt.Println("Error parsing auth context:", err)
  5. return awsutils.StatusResponse(http.StatusInternalServerError), nil
  6. }
  7. queryRequest := handlerInput.GetProfile{
  8. Type: handlerInput.TypeGetProfile,
  9. UserId: authContext.UserID,
  10. }
  11. queryResp := dbModel.User{}
  12. err = LambdaInvoke(context, userServiceArn, queryRequest, &queryResp)
  13. if err != nil {
  14. if ierrError, ok := err.(ierr.Error); ok {
  15. if ierrError.IsSame(user.RecordNotFoundError) {
  16. fmt.Printf("Could not find user profile of userId: %s \n", authContext.UserID)
  17. emptyResp := dbModel.User{}
  18. return awsutils.SwaggerResponse(http.StatusOK, emptyResp.SwaggerModel()), nil
  19. }
  20. }
  21. fmt.Println("Error invoking lambda:", err)
  22. return awsutils.StatusResponse(http.StatusInternalServerError), nil
  23. }
  24. swagUser := queryResp.SwaggerModel()
  25. return awsutils.SwaggerResponse(http.StatusOK, swagUser), nil

}

答案1

得分: 1

https://github.com/stretchr/testify#suite-package

testify具有诸如before、after test等的很好的功能。通过使用它,可以轻松避免复制粘贴代码的相同部分。

英文:

https://github.com/stretchr/testify#suite-package

testify has nice features like before, after test etc. by using this its easy to avoid copy pasting the sam parts of code

huangapple
  • 本文由 发表于 2023年2月13日 22:46:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/75437415.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定