【问题标题】:Mock context.Context to test lambdacontext.FromContext模拟 context.Context 以测试 lambdacontext.FromContext
【发布时间】:2018-05-23 14:18:25
【问题描述】:

我正在使用 aws-sdk-goaws-lambda-go 构建一个 aws lambda,但遇到了一个小问题。

我想测试我的 lambda 处理程序。为此,我需要模拟一个有效的context.Context,其中包含lamdacontext.LambdaContext 的有效属性并满足lambdacontext.FromContext

我似乎找不到构建这种模拟的方法,因为lambdacontext.FromContext 总是返回我_, false

这是我的主要内容,在 events.SNSEvent 事件上有一个简单的处理程序:

package main

import (
    "context"
    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-lambda-go/lambdacontext"
)

func main() {
    lambda.Start(handleRequest)
}

func handleRequest(ctx context.Context, snsEvent events.SNSEvent) error {
    lc, ok := lambdacontext.FromContext(ctx); if !ok {
        // Always false
        ...
        return someErr
    }
    . . .
    return nil
}

这是我对handleRequest 的测试:

package main

import (
    "context"
    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambdacontext"
    "github.com/stretchr/testify/assert"
    "gitlab.easy-network.it/meg/aml-rekognition/testdata"
    "testing"
)

const imgMock = `
{
  \"some_parameter\": \"some_value\"
}`

func TestHandleRequest(t *testing.T) {

    c := context.Background()

    ctxV := context.WithValue(c, "", map[string]interface{}{
        "AwsRequestID" : "some_aws_id",
        "InvokedFunctionArn" : "some_arn",
        "Identity" : lambdacontext.CognitoIdentity{},
        "ClientContext" : lambdacontext.ClientContext{},
    })

    snsEventMock := events.SNSEvent{
        Records: []events.SNSEventRecord{
            {
                SNS: events.SNSEntity{
                    Message: imgMock,
                },
            },
        },
    }

    err := handleRequest(ctxV, snsEventMock)
    assert.NoError(t, err)

}

我还尝试了其他模拟,例如将带有这些参数的结构传递给它等,但我总是得到false。例如,我也试过:

type TestMock struct {
    AwsRequestID string
    InvokedFunctionArn string
    Identity lambdacontext.CognitoIdentity
    ClientContext lambdacontext.ClientContext
}

func TestHandleRequest(t *testing.T) {

    c := context.Background()

    testMock := TestMock{
        AwsRequestID : "some_aws_id",
        InvokedFunctionArn : "some_arn",
        Identity : lambdacontext.CognitoIdentity{},
        ClientContext : lambdacontext.ClientContext{},
    }

    ctxV := context.WithValue(c, "", testMock)

    . . .

}

我查看了FromContext 的来源,我已经摸不着头脑了。

// LambdaContext is the set of metadata that is passed for every Invoke.
type LambdaContext struct {
    AwsRequestID       string
    InvokedFunctionArn string
    Identity           CognitoIdentity
    ClientContext      ClientContext
}

// An unexported type to be used as the key for types in this package.
// This prevents collisions with keys defined in other packages.
type key struct{}

// The key for a LambdaContext in Contexts.
// Users of this package must use lambdacontext.NewContext and 
lambdacontext.FromContext

// instead of using this key directly.
var contextKey = &key{}

// FromContext returns the LambdaContext value stored in ctx, if any.
func FromContext(ctx context.Context) (*LambdaContext, bool) {
    lc, ok := ctx.Value(contextKey).(*LambdaContext)
    return lc, ok
}

当然,即使我只是将context.Background() 传递给它,它也会返回false

我应该如何构建一个有效的context.Contextlambdacontext.FromContext 返回true

【问题讨论】:

    标签: unit-testing go aws-lambda aws-sdk-go aws-lambda-go


    【解决方案1】:

    lambda.FromContext() 检查传递的context.Context 是否在lambdacontext 包中包含一个带有“私有”键的值:

    // An unexported type to be used as the key for types in this package.
    // This prevents collisions with keys defined in other packages.
    type key struct{}
    
    // The key for a LambdaContext in Contexts.
    // Users of this package must use lambdacontext.NewContext and lambdacontext.FromContext
    // instead of using this key directly.
    var contextKey = &key{}
    
    // FromContext returns the LambdaContext value stored in ctx, if any.
    func FromContext(ctx context.Context) (*LambdaContext, bool) {
        lc, ok := ctx.Value(contextKey).(*LambdaContext)
        return lc, ok
    }
    

    您无法访问此密钥,也无法“复制”它(您无法创建与此“私有”密钥相等的值)。

    但是有一个简单的方法,只需使用lambdacontext.NewContext() 来派生一个包含这个键的上下文:

    // NewContext returns a new Context that carries value lc.
    func NewContext(parent context.Context, lc *LambdaContext) context.Context {
        return context.WithValue(parent, contextKey, lc)
    }
    

    所以解决办法:

    ctx := context.Background()
    // Add keys to your liking, then:
    lc := new(lambdacontext.LambdaContext)
    ctx = lambdacontext.NewContext(ctx, lc)
    

    【讨论】:

      猜你喜欢
      • 2020-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多