stretchr / testify

A toolkit with common assertions and mocks that plays nicely with the standard library
MIT License
23.53k stars 1.6k forks source link

Unexpected Method Call #586

Open Tang8330 opened 6 years ago

Tang8330 commented 6 years ago

I'm trying to mock PutItem(*dynamodb.PutItemInput) from AWS Dynamo Package GoDoc here

However, I'm running into a bunch of problems that look something like this:

--- FAIL: TestInvokeEvents (0.00s)
panic:

mock: Unexpected Method Call
-----------------------------

PutItem(*dynamodb.PutItemInput)
        0: {
  Item: {
    account#user: {
      S: "2#b396abf06c7264027a0a2d037d5ccc25"
    },
    time#name#id: {
      S: "1642501880#pageView#9c7e33a0-9a2f-4372-b5bf-ef20a9995d29"
    },
    proto_message: {
      B: <binary> len 348
    }
  },
  TableName: "test_table"
}

The closest call I have is:

PutItem(*dynamodb.PutItemInput)
        0: {
  Item: {
    account#user: {
      S: "2#b396abf06c7264027a0a2d037d5ccc25"
    },
    time#name#id: {
      S: "1642501880#pageView#9c7e33a0-9a2f-4372-b5bf-ef20a9995d29"
    },
    proto_message: {
      B: <binary> len 348
    }
  },
  TableName: "test_table"
}

 [recovered]
    panic:

I've tried a couple of things:

func (db mockDynamo) ExpectPutItem(putItemInput dynamodb.PutItemInput, putItemOutput *dynamodb.PutItemOutput, withErr error) { db.On("PutItem", putItemInput).Return(putItemOutput, withErr).Once() }


- Removed the .Once() to make sure it's not being called multiple times..

What I've found after debugging this for about 5-6 hours is that the issue seemed to be near the `B: <binary> len 348`, which is the bytes result coming from `proto.Marshal`, removing the bytes from being included in the object resulted in the type passing.
Currently I've been able to workaround the issue by using `mock.Anything`, however it would be great to know as to why this is even happening!

Much appreciated and looking forward to chasing this down!
-- Robin
devdinu commented 6 years ago

@Tang8330 I don't know about PutItemInput.Item why its different.

You could mock with argumentMatcher, also assert the argument properly using mock.MatchedBy. Add custom/partial match against the passed argument with an expected one, match only fields which are necessary.

dbMock.On("PutItem", mock.MatchedBy(itemMatcher(expectedItem)))

func itemMatcher(expected *dynamodb.PutItemInput) func(*dynamodb.PutItemInput) bool {
    return func(actual *dynamodb.PutItemInput) {
        return expected.Item["account#user"] == actual.Item["account#user"] &&
                   expected.TableName == actual.TableName // add other necessary fields
    }
}

Hope this helps.

Tang8330 commented 6 years ago

@devdinu Thanks for that, it certainly did help!

It would be great to get to the bottom of why the object was not picked up by the mocking library. Your suggestion has greatly improved my workaround solution and thank you for that.