Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

test unreachable execution path within golang function

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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)
    
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading