Advertisements
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("loan 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 hlpLoanData 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, "loan already exists")
}
>Solution :
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)