Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stricter call matching possible? #36

Open
tommyalatalo opened this issue Dec 26, 2020 · 5 comments
Open

Stricter call matching possible? #36

tommyalatalo opened this issue Dec 26, 2020 · 5 comments

Comments

@tommyalatalo
Copy link
Contributor

I'm having problems with redismock catching other calls for the same operation, which shouldn't be mocked.

For instance, I have a function where I call Scan two times, and I only want to mock the second call to simulate an error being returned. This seems to not work since redismock thinks it should mock the first Scan call as well, even though the key has not been set up to be mocked.

An example in code:

	keys, _, err := client.Scan(ctx, 0, "first:*", 0).Result()
	if err != nil {
		return nil, err
	}

	keys, _, err := client.Scan(ctx, 0, "second:*", 0).Result()
	if err != nil {
		return nil, err
	}

Now if I mock the second call in my test suite like this:

tr.On("Scan", ctx, uint64(0), "second:*", int64(0)).Return(redis.NewScanCmdResult([]string{}, uint64(0), test.err))

redismock will return this error:

Scan(*context.emptyCtx,uint64,string,int64)
		0: (*context.emptyCtx)(0xc00011e010)
		1: 0x0
		2: "first:*"
		3: 0

The closest call I have is: 

Scan(*context.emptyCtx,uint64,string,int64)
		0: (*context.emptyCtx)(0xc00011e010)
		1: 0x0
		2: "second:*"
		3: 0

I'm using NewNiceMock to create my test client. Is there a way to set the mocking to be stricter, so that any calls that are not defined with the .On function will be completely ignored and not fuzzy-matched or whatever is happening here now?

The behavior that I'm getting now is requiring me to mock both calls, which means that the first call can't use miniredis to get real data, which is of course not desirable.

@elliotchance
Copy link
Owner

I can't test it right now, but I'm pretty sure testify lets you do this by providing different expecations:

tr.On("Scan", ctx, uint64(0), "first:*", int64(0)).
  Return(redis.NewScanCmdResult([]string{}, uint64(0), test.err))

tr.On("Scan", ctx, uint64(0), "second:*", int64(0)).
  Return(redis.NewScanCmdResult([]string{}, uint64(0), test.err))

Alternatively, you could use DoAndReturn() if you wanted to handle the arguments dynamically.

@tommyalatalo
Copy link
Contributor Author

tommyalatalo commented Dec 28, 2020

I can't test it right now, but I'm pretty sure testify lets you do this by providing different expecations:

tr.On("Scan", ctx, uint64(0), "first:*", int64(0)).
  Return(redis.NewScanCmdResult([]string{}, uint64(0), test.err))

tr.On("Scan", ctx, uint64(0), "second:*", int64(0)).
  Return(redis.NewScanCmdResult([]string{}, uint64(0), test.err))

Alternatively, you could use DoAndReturn() if you wanted to handle the arguments dynamically.

I don't see any difference in the example mocks you defined here except for the path in redis?
Not sure what you wanted to point out with those two mocks, that's kind of another example of my problem, if you want to mock second but not first it doesn't seem to work to just omit the mock for first.

I would be really interested in how you would use DoAndReturn() to handle this case. The best, and preferred solution, I think would still be to have redismock strictly only mock the calls you define with .On(), if it's not already doing that and I'm misunderstanding the way it works.

@elliotchance
Copy link
Owner

I see, I didn't understand you at first. I haven't tested these, but this should do what you want:

tr.On("Scan", ctx, uint64(0), mock.Anything, int64(0)).
  Times(2).
  DoAndReturn(func (ctx context.Context, cursor uint64, match string, count int64) *redis.ScanCmd {
      if match == "second:*" {
        return redis.NewScanCmdResult([]string{}, uint64(0), test.err)
      }

      return client.Scan(ctx, cursor, match, count)
  })

Or, if you prefer to have separate expections:

tr.On("Scan", ctx, uint64(0), "first:*", int64(0)).
  DoAndReturn(client.Scan)

tr.On("Scan", ctx, uint64(0), "second:*", int64(0)).
  Return(redis.NewScanCmdResult([]string{}, uint64(0), test.err))

@tommyalatalo
Copy link
Contributor Author

I tried to use this one of your examples:

tr.On("Scan", ctx, uint64(0), "first:*", int64(0)).DoAndReturn(client.Scan)

This can't be done though, because `type *mock.Call has no field or method DoAndReturn?

@elliotchance
Copy link
Owner

Sorry, I got my mocking frameworks mixed up. Unfortunatly, testify mock package doesn't have a way to dynamically return, but a quick search brought this up: stretchr/testify#350 which will solve your problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants