golang / mock

GoMock is a mocking framework for the Go programming language.
Apache License 2.0
9.31k stars 609 forks source link

Using mock gRPC with parallel testing #466

Closed DuncanGener8 closed 4 years ago

DuncanGener8 commented 4 years ago

I'm not really sure if this is a feature request, a bug-report of something else in between, so my apologies if this is not filed correctly.

I've been using this package for mocking out our gRPC end-points in our unit testing, but I've hit a bit of a snag in a new type of test case that I'm attempting to write. The gist of it is as follows:

I'm trying to demonstrate that our method "foo", which invokes multiple gRPC end-points to fetch data will treat 2 concurrent invocations differently, depending on the data returned from one of the gRPC end-points. You can think of the code being something like this:

func foo() {
// -- some stuff happens --
  result, _ := fetchDataViaGRPC(input)
  if result.Value < 1 {
    time.Sleep(time.Second)
  } 
  // else continue
// -- stuff continues happening --
}

Essentially, the 2 requests are competing for a specific resource, and one request will be favoured less/delayed if a certain result is provided from the other side of the gRPC.

When executing our test cases in a linear fashion, this works fine since I can setup the mock controllers to render back the exact data that I need for my test case to execute. However, when executing tests in parallel, it seems like I can't orchestrate this as well as I need to.

Specifically, I'd like to configure the mock controller to expect 2 invocations of 'fetchDataViaGRPC', and if input is A then return X, but if input is B then return Y.

If I set it up in the usual way, the test ends up bailing early because the mock controller is expecting A, but receiving B. If I set the mock controllers to expect 'gomock.Any()' rather than a specific input - it negates the purpose of the test. Using the 'MinTimes' and 'MaxTimes' functions will also work, but these methods also prevents me from being able to customise the outputs to be generated.

I know that this is a bit of an edge-case for the project, and that go's parallel testing is a bit funky at the best of times, but if you know of a solution for this I'd greatly appreciate your help.

Kind Regards, Duncan

cvgw commented 4 years ago

Apologies if I am misunderstanding;

It sounds like what you want should work today; foo.EXPECT().Bar("a", 1).Return("baz") foo.EXPECT().Bar("b",2).Return("boom")

Does that not work for your use case?

codyoss commented 4 years ago

Closing due to lack of response.

DuncanGener8 commented 4 years ago

Hi @cvgw ,

Apologies for the delay in my response, I missed the original notification mail 2 weeks ago.

The method you're describing doesn't seem to work for me. When the two tests run in parallel, it seems like Case 1, which is: foo.EXPECT().Bar("a",1).Return("baz")

Receives a request of the form b instead, which causes the tests to bail.

Regards, Duncan

cvgw commented 4 years ago

@DuncanGener8 I think I may have misunderstood your original use case.

Are you using the same mock across two tests?

IE

func TestFoo(t *testing.T) {
  ctrl := gomock.NewController(t)
  mockFoo := NewMockFoo(ctrl)

  t.Run("subtest 1", func(t *testing.T) {
    // do something with mockFoo
  })

  t.Run("subtest 2", func(t *testing.T) {
    // do something with mockFoo
  })
}

or some variation of that using pkg lvl variable, etc.