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

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

test unreachable execution path within golang function

问题

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

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

func (c *MyContract) CreateAsset(ctx contractapi.TransactionContextInterface, values string) (bool, error) {
	
	doctype := "myAsset"
	txData := []byte(values)
	docData := new(DocData)
	docData.DataType = doctype

	// validate json input & map to struct
	err := json.Unmarshal(txData, &docData)
	if err != nil {
		return false, fmt.Errorf("failed docData unmarshalling: %s", err.Error())
	}

	docKey, _ := createKey(ctx, doctype, []string{docData.Key1, docData.Key2})

	exists, err := c.DocExists(ctx, docKey)
	if err != nil {
		return false, fmt.Errorf("could not read from world state %s", err)
	} else if exists {
		return false, fmt.Errorf("asset already exists")
	}

	txBytes, err := json.Marshal(docData)
	if err != nil {
		return false, fmt.Errorf("failed docData bytes marshalling: %s", err.Error())
	}

	return true, ctx.GetStub().PutState(docKey, txBytes)
}

这是我当前的测试代码:

func TestCreateAsset(t *testing.T) {
	var err error
	ctx, _ := setupStub()
	c := new(MyContract)


	_, err = c.CreateAsset(ctx, "{")
	assert.EqualError(t, err, "failed unmarshalling: unexpected end of JSON input", "testing malformed json")

	_, err = c.CreateAsset(ctx, "{\"key1\":\"mis\",\"key2\":\"sing\"}")
	assert.EqualError(t, err, "could not read from world state some failure")

	_, err = c.CreateAsset(ctx, "{\"key1\":\"001\",\"key2\":\"002\"}")
	assert.EqualError(t, err, "asset already exists")

}
英文:

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?

func (c *MyContract) CreateAsset(ctx contractapi.TransactionContextInterface, values string) (bool, error) {
	
	doctype := "myAsset"
	txData := []byte(values)
	docData := new(DocData)
	docData.DataType = doctype

	// validate json input & map to struct
	err := json.Unmarshal(txData, &docData)
	if err != nil {
		return false, fmt.Errorf("failed docData unmarshalling: %s", err.Error())
	}

	docKey, _ := createKey(ctx, doctype, []string{docData.Key1, docData.Key2})

	exists, err := c.DocExists(ctx, docKey)
	if err != nil {
		return false, fmt.Errorf("could not read from world state %s", err)
	} else if exists {
		return false, fmt.Errorf("asset already exists")
	}

	txBytes, err := json.Marshal(docData)
	if err != nil {
		return false, fmt.Errorf("failed docData bytes marshalling: %s", err.Error())
	}

	return true, ctx.GetStub().PutState(docKey, txBytes)
}

This is my current test:

func TestCreateAsset(t *testing.T) {
	var err error
	ctx, _ := setupStub()
	c := new(MyContract)


	_, err = c.CreateAsset(ctx, "{")
	assert.EqualError(t, err, "failed unmarshalling: unexpected end of JSON input", "testing malformed json")

	_, err = c.CreateAsset(ctx, "{\"key1\":\"mis\",\"key2\":\"sing\"}")
	assert.EqualError(t, err, "could not read from world state some failure")

	_, err = c.CreateAsset(ctx, "{\"key1\":\"001\",\"key2\":\"002\"}")
	assert.EqualError(t, err, "asset already exists")

}

答案1

得分: 4

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

type DocData struct {
    ...
    Test       interface{} `json:"test,omitempty"`
}

...

var induceFailure interface{}

...

docData.Test = induceFailure
txBytes, err := json.Marshal(docData)
if err != nil {
    return false, fmt.Errorf("failed docData bytes marshalling: %s", err.Error())
}

...

func TestMarshlFail(t *testing.T) {
    induceFailure = make(chan struct{})
    defer func() {
       induceFailure = nil
    }()
    ...

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

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

  • 包装错误而不是将错误转换为字符串:
return nil, fmt.Errorf("failed docData unmarshalling: %w", err)
  • 因为docData是一个指针,在解组时不需要取值的地址。
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:

type DocData struct {
	…
	Test       interface{} `json:"test,omitempty"`
}

…

var induceFailure interface{}

…

docData.Test = induceFailure
txBytes, err := json.Marshal(docData)
if err != nil {
    return false, fmt.Errorf("failed docData bytes marshalling: %s", err.Error())
}

…

func TestMarshlFail(t *testing.T) {
    induceFailure = make(chan struct{})
    defer func() {
       induceFailure= nil
    }()
    …

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:

    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.

    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:

确定