Skip to content

Commit 145af2e

Browse files
committed
Conditionally initalise Worker state
This commit updates the `Worker` `GenServer` so that the `handle_continue/2` callback is only invoked when the service is set to `DefaultService`. This module already knows about `DefaultService` so this seems slightly better than pattern matching on `MockService`, however if there are alternative "production" services, other than `DefaultService` the `Worker.init/1` would need to change. Because we are unable to call `handle_continue/2` directly from the `worker_test.ex` we need to add `initialise_foo/0` which finds the pid and sends the `:initialise_foo` message. This same message is also used in the `handle_continue/2` callback. The tests can be run using: ``` mix test --only mocked_service ```
1 parent 3d78103 commit 145af2e

File tree

2 files changed

+33
-32
lines changed

2 files changed

+33
-32
lines changed

lib/example/worker.ex

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,7 @@ defmodule Example.Worker do
33

44
alias Example.DefaultService
55

6-
# When using ElixirLS, defining the service at compile time will result in an
7-
# error because ElixirLS always compiles using MIX_ENV=test which mean @service
8-
# will always be set to MockService, which does not have `foo/0`
9-
# @service Application.get_env(:example, :service, DefaultService)
10-
# @service DefaultService
11-
12-
def service() do
13-
Application.get_env(:example, :service, DefaultService)
14-
end
6+
@service Application.get_env(:example, :service, DefaultService)
157

168
def start_link(init_arg \\ []) do
179
GenServer.start_link(__MODULE__, init_arg, name: __MODULE__)
@@ -21,28 +13,33 @@ defmodule Example.Worker do
2113
GenServer.call(__MODULE__, :get_foo)
2214
end
2315

16+
def initialise_foo() do
17+
pid = Process.whereis(__MODULE__)
18+
send(pid, :initialise_foo)
19+
end
20+
2421
def init(_init_arg) do
2522
initial_state = "no foo for you"
26-
{:ok, initial_state, {:continue, :get_foo_from_service}}
23+
24+
case @service do
25+
DefaultService ->
26+
{:ok, initial_state, {:continue, :get_foo_from_service}}
27+
28+
_ ->
29+
{:ok, initial_state}
30+
end
31+
end
32+
33+
def handle_continue(:get_foo_from_service, state) do
34+
send(self(), :initialise_foo)
35+
36+
{:noreply, state}
2737
end
2838

29-
def handle_continue(:get_foo_from_service, _state) do
30-
# And here lies the problem. We want to call our service to get
31-
# whatever inital state it provides, but in doing so, we break
32-
# in the test environment because the MockService doesn't have
33-
# a function called `foo/0` until it can be defined in the expects
34-
# block within the test - by that time, this code has already
35-
# been executed because this GenServer is part of the staticly
36-
# defined supervision tree in `application.ex`.
37-
38-
value_of_foo =
39-
if function_exported?(service(), :foo, 0) do
40-
service().foo()
41-
else
42-
"#{inspect(service())} does not support foo"
43-
end
44-
45-
{:noreply, value_of_foo}
39+
def handle_info(:initialise_foo, _state) do
40+
IO.inspect("initialising foo now")
41+
new_state = @service.foo()
42+
{:noreply, new_state}
4643
end
4744

4845
def handle_call(:get_foo, _from, state) do

test/worker_test.exs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,33 @@ defmodule Example.WorkerTest do
44
alias Example.Worker
55

66
describe "default service" do
7+
@tag :default_service
78
test "returns default service foo" do
89
assert Worker.get_foo() =~ ~s(default says foo)
910
end
1011
end
1112

1213
describe "mocked service" do
14+
setup :verify_on_exit!
15+
1316
setup do
14-
# Normally you would add this to `test_helper.ex`, or `support/mocks.ex
17+
# Normally you would add this to `test_helper.ex`, or `support/mocks.ex`
1518
Mox.defmock(Example.MockService, for: Example.ServiceBehaviour)
1619

17-
Example.MockService
18-
|> expect(:foo, fn -> "setup all says foo" end)
20+
# Example.MockService
21+
# |> expect(:foo, fn -> "setup all says foo" end)
1922

2023
:ok
2124
end
2225

23-
setup :verify_on_exit!
24-
26+
@tag :mocked_service
2527
test "returns mocked service foo" do
2628
Example.MockService
2729
|> expect(:foo, fn -> "mock says foo" end)
2830
|> allow(self(), Process.whereis(Worker))
2931

32+
Worker.initialise_foo()
33+
3034
assert Worker.get_foo() =~ ~s(mock says foo)
3135
end
3236
end

0 commit comments

Comments
 (0)