测试在 Golang 函数中无法到达的执行路径。

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

test unreachable execution path within golang function

问题

我正在为下面的链码编写单元测试,以覆盖所有的执行路径。但是我没有找到一种方法来触发 JSON.Marshal 失败的情况。

我该如何传递一个能够通过 json.Unmarshal,但无法通过 json.Marshal 的值?

  1. func (c *MyContract) CreateAsset(ctx contractapi.TransactionContextInterface, values string) (bool, error) {
  2. doctype := "myAsset"
  3. txData := []byte(values)
  4. docData := new(DocData)
  5. docData.DataType = doctype
  6. // validate json input & map to struct
  7. err := json.Unmarshal(txData, &docData)
  8. if err != nil {
  9. return false, fmt.Errorf("failed docData unmarshalling: %s", err.Error())
  10. }
  11. docKey, _ := createKey(ctx, doctype, []string{docData.Key1, docData.Key2})
  12. exists, err := c.DocExists(ctx, docKey)
  13. if err != nil {
  14. return false, fmt.Errorf("could not read from world state %s", err)
  15. } else if exists {
  16. return false, fmt.Errorf("asset already exists")
  17. }
  18. txBytes, err := json.Marshal(docData)
  19. if err != nil {
  20. return false, fmt.Errorf("failed docData bytes marshalling: %s", err.Error())
  21. }
  22. return true, ctx.GetStub().PutState(docKey, txBytes)
  23. }

这是我当前的测试代码:

  1. func TestCreateAsset(t *testing.T) {
  2. var err error
  3. ctx, _ := setupStub()
  4. c := new(MyContract)
  5. _, err = c.CreateAsset(ctx, "{")
  6. assert.EqualError(t, err, "failed unmarshalling: unexpected end of JSON input", "testing malformed json")
  7. _, err = c.CreateAsset(ctx, "{\"key1\":\"mis\",\"key2\":\"sing\"}")
  8. assert.EqualError(t, err, "could not read from world state some failure")
  9. _, err = c.CreateAsset(ctx, "{\"key1\":\"001\",\"key2\":\"002\"}")
  10. assert.EqualError(t, err, "asset already exists")
  11. }
英文:

I'm writing unit-tests to cover all execution paths in the fabric chaincode below. But I don't see a way to reach the JSON.Marshall failing path.

How can I pass a value that passes to json.Unmarshal but it fails to json.Marshal?

  1. func (c *MyContract) CreateAsset(ctx contractapi.TransactionContextInterface, values string) (bool, error) {
  2. doctype := "myAsset"
  3. txData := []byte(values)
  4. docData := new(DocData)
  5. docData.DataType = doctype
  6. // validate json input & map to struct
  7. err := json.Unmarshal(txData, &docData)
  8. if err != nil {
  9. return false, fmt.Errorf("failed docData unmarshalling: %s", err.Error())
  10. }
  11. docKey, _ := createKey(ctx, doctype, []string{docData.Key1, docData.Key2})
  12. exists, err := c.DocExists(ctx, docKey)
  13. if err != nil {
  14. return false, fmt.Errorf("could not read from world state %s", err)
  15. } else if exists {
  16. return false, fmt.Errorf("asset already exists")
  17. }
  18. txBytes, err := json.Marshal(docData)
  19. if err != nil {
  20. return false, fmt.Errorf("failed docData bytes marshalling: %s", err.Error())
  21. }
  22. return true, ctx.GetStub().PutState(docKey, txBytes)
  23. }

This is my current test:

  1. func TestCreateAsset(t *testing.T) {
  2. var err error
  3. ctx, _ := setupStub()
  4. c := new(MyContract)
  5. _, err = c.CreateAsset(ctx, "{")
  6. assert.EqualError(t, err, "failed unmarshalling: unexpected end of JSON input", "testing malformed json")
  7. _, err = c.CreateAsset(ctx, "{\"key1\":\"mis\",\"key2\":\"sing\"}")
  8. assert.EqualError(t, err, "could not read from world state some failure")
  9. _, err = c.CreateAsset(ctx, "{\"key1\":\"001\",\"key2\":\"002\"}")
  10. assert.EqualError(t, err, "asset already exists")
  11. }

答案1

得分: 4

json.Marshal函数在无法对数据中的某个值进行编组时会失败。唯一引发失败的方法是在测试期间引入一个用于测试的字段,并在测试期间插入一个错误的值:

  1. type DocData struct {
  2. ...
  3. Test interface{} `json:"test,omitempty"`
  4. }
  5. ...
  6. var induceFailure interface{}
  7. ...
  8. docData.Test = induceFailure
  9. txBytes, err := json.Marshal(docData)
  10. if err != nil {
  11. return false, fmt.Errorf("failed docData bytes marshalling: %s", err.Error())
  12. }
  13. ...
  14. func TestMarshlFail(t *testing.T) {
  15. induceFailure = make(chan struct{})
  16. defer func() {
  17. induceFailure = nil
  18. }()
  19. ...

可能不值得费力在那行代码上进行测试覆盖。

与问题无关,这里有一些改进你的代码的建议。

  • 包装错误而不是将错误转换为字符串:
  1. return nil, fmt.Errorf("failed docData unmarshalling: %w", err)
  • 因为docData是一个指针,在解组时不需要取值的地址。
  1. err := json.Unmarshal(txData, docData)
英文:

The json.Marshal function fails when a value in the data cannot be marshaled. The only way to induce a failure is to introduce a field for testing and slip in a bad value during a test:

  1. type DocData struct {
  2. Test interface{} `json:"test,omitempty"`
  3. }
  4. var induceFailure interface{}
  5. docData.Test = induceFailure
  6. txBytes, err := json.Marshal(docData)
  7. if err != nil {
  8. return false, fmt.Errorf("failed docData bytes marshalling: %s", err.Error())
  9. }
  10. func TestMarshlFail(t *testing.T) {
  11. induceFailure = make(chan struct{})
  12. defer func() {
  13. induceFailure= nil
  14. }()

It's probably not worth the hassle to get the test coverage on that line of code.

Unrelated to the question at hand, here are some improvements for your code.

  • Wrap errors instead of converting the errors to strings:

    1. return nil, fmt.Errorf("failed docData unmarshalling: %w", err)
  • Because docData is a pointer, there's no need to take the address of the value when unmarshaling.

    1. err := json.Unmarshal(txData, docData)

huangapple
  • 本文由 发表于 2021年11月5日 23:14:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/69855468.html
匿名

发表评论

匿名网友

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

确定