Unit tests panic when returning dereferenced struct attribute rather than struct

偶尔善良 提交于 2021-02-11 18:24:40

问题


How does one go about testing a function that is returning a struct attribute that is of type string or number, rather than the struct itself?

I am trying to test the Lambda Code block with the Test Code block.

In Lambda Code block below, I am returning *resp.UserPoolClient.ClientSecret which dereferences to a string, rather than the *string.

When I run my test, I believe I get a panic error as *resp.UserPoolClient.ClientSecret is nil in the debugger.

Is my returning of the de-referenced attribute the wrong approach? Wondering if I'm better off just returning the whole resp object, not de-referenced? I was doing it this way as I didn't need to modify the values at all, just needed copies available for reference.

Lambda Code

package main
 
import (
    "fmt"
    "log"
    "os"
 
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/awserr"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-sdk-go/aws/session"
    cidp "github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
    cidpif "github.com/aws/aws-sdk-go/service/cognitoidentityprovider/cognitoidentityprovideriface"
    util "github.com/sean/repo/internal/util"
)
 
type application struct {
    config configuration
}
 
type configuration struct {
    ClientPoolID string
    UserPoolID   string
    idp          cidpif.CognitoIdentityProviderAPI
}
 
func (app application) getUserPoolClientSecret() (string, error) {
    input := &cidp.DescribeUserPoolClientInput{
        UserPoolId: aws.String(app.config.UserPoolID),
        ClientId:   aws.String(app.config.ClientPoolID),
    }
 
    resp, err := app.config.idp.DescribeUserPoolClient(input)
    if err != nil {
        if aerr, ok := err.(awserr.Error); ok {
            log.Printf("[ERROR] %v", aerr.Error())
        } else {
            log.Printf("[ERROR] %v", err.Error())
        }
        return "", err
    }
    log.Println("[INFO] Obtained user pool client secret successfully")
    return *resp.UserPoolClient.ClientSecret, nil
}

// omitted for brevity
 
func main() {
    config := configuration{
        ClientPoolID: os.Getenv("CLIENT_POOL_ID"),
        UserPoolID:   os.Getenv("USER_POOL_ID"),
        idp:          cidp.New(session.Must(session.NewSession())),
    }
 
    app := application{config: config}
 
    lambda.Start(app.handler) // handler() calls app.getUserPoolClientSecret
}

Test Code

package main
 
import (
    "testing"
 
    cidp "github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
    cidpif "github.com/aws/aws-sdk-go/service/cognitoidentityprovider/cognitoidentityprovideriface"
)
 
 
type mockDescribeUserPoolClient struct {
    cidpif.CognitoIdentityProviderAPI
    Response *cidp.DescribeUserPoolClientOutput
    Error    error
}
 
func (m mockDescribeUserPoolClient) DescribeUserPoolClient(*cidp.DescribeUserPoolClientInput) (*cidp.DescribeUserPoolClientOutput, error) {
    return m.Response, nil
}
 
func TestGetUserPoolClientSecret(t *testing.T) {
    t.Run("Successfully obtained client pool secret", func(t *testing.T) {
        idpMock := mockDescribeUserPoolClient{
            Response: &cidp.DescribeUserPoolClientOutput{},
            Error:    nil,
        }
 
        app := application{config: configuration{
            ClientPoolID: "test",
            UserPoolID:   "test",
            idp:          idpMock,
        }}
 
        _, err := app.getUserPoolClientSecret()
        if err != nil {
            t.Fatal("App secret should have been obtained")
        }
    })
}

回答1:


Is my returning of the de-referenced attribute the wrong approach?

Personally, I don't think it is wrong. I would do the same. But more seasoned Go developers might be able to contribute a more nuanced and detailed answer here.

In regards to the panic, I think the issue is that the mock you create does not return all the required information.

Your mock:

idpMock := mockDescribeUserPoolClient{
    Response: &cidp.DescribeUserPoolClientOutput{},
    Error:    nil,
}

You only create an "empty" instance of DescribeUserPoolClientOutput. But the code that you want to test does access two children, that you did not define:

  1. UserPoolClient (struct reference)
  2. ClientSecret (string reference)

Your code:

*resp.UserPoolClient.ClientSecret

So your mock needs to mock those as well:

idpMock := mockDescribeUserPoolClient{
    Response: &cidp.DescribeUserPoolClientOutput{
        UserPoolClient: &cidp.UserPoolClientType{
            ClientSecret: aws.String("example-secret")
        }
    },
    Error:    nil,
}

That should fix your panic.



来源:https://stackoverflow.com/questions/65767851/unit-tests-panic-when-returning-dereferenced-struct-attribute-rather-than-struct

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!