Skip to content

Commit 2a44fea

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 ```
1 parent 578fddd commit 2a44fea

3 files changed

Lines changed: 42 additions & 42 deletions

File tree

lib/example/worker.ex

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@ 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-
126
def service() do
137
Application.get_env(:example, :service, DefaultService)
148
end
@@ -21,28 +15,33 @@ defmodule Example.Worker do
2115
GenServer.call(__MODULE__, :get_foo)
2216
end
2317

18+
def initialise_foo() do
19+
pid = Process.whereis(__MODULE__)
20+
send(pid, :initialise_foo)
21+
end
22+
2423
def init(_init_arg) do
2524
initial_state = "no foo for you"
26-
{:ok, initial_state, {:continue, :get_foo_from_service}}
27-
end
28-
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}
25+
26+
case service() do
27+
DefaultService ->
28+
{:ok, initial_state, {:continue, :get_foo_from_service}}
29+
30+
_ ->
31+
{:ok, initial_state}
32+
end
33+
end
34+
35+
def handle_continue(:get_foo_from_service, state) do
36+
send(self(), :initialise_foo)
37+
38+
{:noreply, state}
39+
end
40+
41+
def handle_info(:initialise_foo, _state) do
42+
IO.inspect("initialising foo now")
43+
new_state = service().foo()
44+
{:noreply, new_state}
4645
end
4746

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

test/test_helper.exs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
Mox.defmock(Example.MockService, for: Example.ServiceBehaviour)
2+
13
ExUnit.start()

test/worker_test.exs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,30 @@ defmodule Example.WorkerTest do
33
import Mox
44
alias Example.Worker
55

6-
describe "default service" do
7-
test "returns default service foo" do
8-
assert Worker.get_foo() =~ ~s(default says foo)
9-
end
6+
setup do
7+
Worker.initialise_foo()
8+
9+
:ok
1010
end
1111

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

14+
describe "mocked service" do
15+
@tag :mocked_service
16+
test "returns mocked service foo" do
1717
Example.MockService
18-
|> expect(:foo, fn -> "setup all says foo" end)
18+
|> expect(:foo, fn -> "mock says foo" end)
19+
|> allow(self(), Worker)
1920

20-
:ok
21+
assert Worker.get_foo() =~ ~s(mock says foo)
2122
end
2223

23-
setup :verify_on_exit!
24-
25-
test "returns mocked service foo" do
24+
test "returns mocked service moo" do
2625
Example.MockService
27-
|> expect(:foo, fn -> "mock says foo" end)
28-
|> allow(self(), Process.whereis(Worker))
26+
|> expect(:foo, fn -> "mock says moo" end)
27+
|> allow(self(), Worker)
2928

30-
assert Worker.get_foo() =~ ~s(mock says foo)
29+
assert Worker.get_foo() =~ ~s(mock says moo)
3130
end
3231
end
3332
end

0 commit comments

Comments
 (0)