Home > OS >  test unreachable execution path within golang function
test unreachable execution path within golang function

Time:11-06

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")

}

CodePudding user response:

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)
    
  • Related