英文:
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)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论