dashbitco / mox

Mocks and explicit contracts in Elixir
1.35k stars 77 forks source link

Mocking a process spawned by a GenServer #29

Closed sashaafm closed 6 years ago

sashaafm commented 6 years ago

We're trying to mock a GenServer which spawns a process (also a GenServer). This spawned process is a client which stays connected to a broker and we pass its PID around in the GenServer's state.

We're trying to use mox for this, however, by crating a mock for the client process our tests crash on application startup we the following error:

** (Mix) Could not start application <redacted>: Application.start(:normal, []) returned an error: shutdown: failed to start child: Publisher
    ** (EXIT) an exception was raised:
        ** (Mox.UnexpectedCallError) no expectation defined for ClientMock.start_link/0 in process #PID<0.317.0>
            (mox) lib/mox.ex:438: Mox.__dispatch__/4
            (<redacted>) lib/publisher.ex:22: Publisher.init/1
            (stdlib) gen_server.erl:365: :gen_server.init_it/2
            (stdlib) gen_server.erl:333: :gen_server.init_it/6
            (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3

Here's a Gist which should be enough to reproduce the problem: Gist

It seems mox is expecting an expect to be defined for ClientMock.start_link/0 not in the test case, but actually in the application code itself? Are we doing something wrong or is this a shortcoming of the library?

Thanks, if you need anymore info we'll be glad to provide it.

josevalim commented 6 years ago

If the process is started when the application starts and you are configuring that process to use the mock at compile time, then it will try to invoke the mock when the application starts. There are a couple options to solve this.

The first one is to use mix test --no-start and start the application manually in test_helper.exs after the mock is configured. You can use a mix alias to always use the --no-start flag.

Another option is to read the configuration at runtime or lazily start the process or not start the processing during test via a configuration.

None of this is specific to Mox though, you need to find the best trade-offs in the system in terms of test confidence and guarantees.

chulkilee commented 6 years ago

I have a GenServer which fetches remote API inside init/1 - so I cannot wait for pid and allow(self(), pid). I ended up with 1) extracting fetching into own function, and 1) adding skip_load option when starting the GenServer.

Or.. I may turn off async test and globally allow it..