代码之家  ›  专栏  ›  技术社区  ›  codewarrior

如何将Gomock与AWS SDK结合使用?

  •  0
  • codewarrior  · 技术社区  · 1 年前

    例如,当我使用gomock测试使用DynamoDB的代码时,如下所示:

        mockDynamoDB := awsmock.NewMockDynamoDBAPI(ctrl)
        mockDynamoDBClient.EXPECT().PutItemWithContext(gomock.Any(),
           gomock.Any() // Here I should use a custom matcher for comparing PutItemInput
    ).Return(&dynamodb.PutItemOutput{...}, nil).Times(1)
    
       // do something which will internally call PutItemWithContext
    

    避免使用 gomock.Any() 在所有地方,我认为我应该为 PutItemInput 然而,这似乎有点困难,因为 PutItem输入 序列化很复杂 Item map[string]*AttributeValue ,我需要像这样逐一比较,但我不喜欢这个实现。

    func (piim dynamodbPutItemInputEqualMatcher) Matches(x interface{}) bool {
        input, ok := x.(*dynamodb.PutItemInput)
        if !ok {
            return false
        }
    
        if input.TableName != nil && piim.expectedInput.TableName != nil && *input.TableName != *pie.expectedInput.TableName {
            return false
        }
    
        for attributeName, attributeValue := range input.Item {
            exptectedAttributeValue, ok := piim.expectedInput.Item[attributeName]
            if !ok {
                return false
            }
    
            if attributeValue.N != nil && exptectedAttributeValue.N != nil && *attributeValue.N != *exptectedAttributeValue.N {
                return false
            }
            if attributeValue.S != nil && exptectedAttributeValue.S != nil && *attributeValue.S != *exptectedAttributeValue.S {
                return false
            }
            if attributeValue.BOOL != nil && exptectedAttributeValue.BOOL != nil && *attributeValue.BOOL != *exptectedAttributeValue.BOOL {
                return false
            }
            if attributeValue.NULL != nil && exptectedAttributeValue.NULL != nil && *attributeValue.NULL != *exptectedAttributeValue.NULL {
                return false
            }
    
            if attributeValue.B != nil || attributeValue.NS != nil || attributeValue.SS != nil || attributeValue.BS != nil || attributeValue.L != nil || attributeValue.M != nil {
                return false
            }
        }
    
        return true
    }
    

    DynamoDB还有其他API,如 GetItem , ScanPages 等等,。我需要为每种类型的输入结构实现自定义匹配器。

    我们是否有更好的方法将gomock与AWS结合使用?谢谢。

    ===========================================

    我不认为我的案例过度使用mocking,我的代码不仅仅是DynamoDB调用的简单包装器,它实现了我自己的业务逻辑,如果需要,最后会写入DynamoDB。我想将DynamoDB与我的单元测试隔离开来有几个原因。

    (1) 要使用真正的DynamoDB进行测试,我需要在Jenkins中运行单元测试之前设置有效的creds,并保持creds的维护,此外,执行单元测试的环境必须有互联网连接,这很难,有时甚至是不可能的。

    (2) 我只是想在单元测试中验证我的业务逻辑。例如,函数需要首先验证输入参数,当用户输入无效时,立即返回错误,单元测试应该涵盖这一点,这与DynamoDB完全无关。这里的测试用例主要关注我的业务逻辑,即当输入不同的输入时,业务逻辑是否按预期工作,而不是验证DynamoDB是否可靠,因此可以合理地假设DynamoDB在我的单元测试中始终按预期运行。

    (3) 集成测试的范围/目标是在将我的业务逻辑与真实的DynamoDB结合在一起时测试功能,我们已经对此进行了综合测试,单元测试只是单元测试。如果我在测试中使用真实的DynamoDB,以防DynamoDB发生故障,或者Jenkins和DynamoDB之间的连接中断,UT失败,我不希望我的UT受到这种故障的影响。

    0 回复  |  直到 1 年前
        1
  •  0
  •   eik    1 年前

    这似乎是一个例子 overusing mocks 。你将代码的实现细节泄露到测试中,只有当你以编写代码的方式使用模拟时,测试才有效,这使得重构变得不可能。

    你不会测试你是否对DynamoDB进行了正确的调用(为此,你必须重新实现DynamoDB),但如果你正在进行的调用是错误的,你也必须修复你的测试。

    因此,你在这里要做的就是返回预期的结果,看看你的代码是否正常工作,只有当你的模拟不足并收到意外查询时才会出错——这是你的模拟的问题,而不是你的代码的问题。

    你的模拟不应该也不能验证所有参数——如果不重新实现DynamoDB或严格限制有效调用集,只有当前实现有效,这是不可能的。