From 1661285322d4bd972075736fbd84b241c2a66316 Mon Sep 17 00:00:00 2001 From: Keith Salisbury Date: Wed, 29 Jan 2020 23:57:32 +0000 Subject: [PATCH] Hand crafted test services This version sets up services in the test by updating the environment variable from within the test case. To make the worker use the new service requires restarting the worker. This approach isn't compatible with Mox, and the assertion is based on values hard coded in the custom test services which feels kind of clunky. --- config/test.exs | 2 -- lib/example/worker.ex | 24 +++--------------------- test/support/mock_service.ex | 11 ----------- test/support/test1_example.ex | 7 +++++++ test/support/test2_example.ex | 7 +++++++ test/worker_test.exs | 31 ++++++++++++++----------------- 6 files changed, 31 insertions(+), 51 deletions(-) delete mode 100644 test/support/mock_service.ex create mode 100644 test/support/test1_example.ex create mode 100644 test/support/test2_example.ex diff --git a/config/test.exs b/config/test.exs index e7da6b4..d2d855e 100644 --- a/config/test.exs +++ b/config/test.exs @@ -1,3 +1 @@ use Mix.Config - -config :example, service: Example.MockService diff --git a/lib/example/worker.ex b/lib/example/worker.ex index aed9473..6578aa8 100644 --- a/lib/example/worker.ex +++ b/lib/example/worker.ex @@ -3,13 +3,7 @@ defmodule Example.Worker do alias Example.DefaultService - # When using ElixirLS, defining the service at compile time will result in an - # error because ElixirLS always compiles using MIX_ENV=test which mean @service - # will always be set to MockService, which does not have `foo/0` - # @service Application.get_env(:example, :service, DefaultService) - # @service DefaultService - - def service() do + def service do Application.get_env(:example, :service, DefaultService) end @@ -23,24 +17,12 @@ defmodule Example.Worker do def init(_init_arg) do initial_state = "no foo for you" + {:ok, initial_state, {:continue, :get_foo_from_service}} end def handle_continue(:get_foo_from_service, _state) do - # And here lies the problem. We want to call our service to get - # whatever inital state it provides, but in doing so, we break - # in the test environment because the MockService doesn't have - # a function called `foo/0` until it can be defined in the expects - # block within the test - by that time, this code has already - # been executed because this GenServer is part of the staticly - # defined supervision tree in `application.ex`. - - value_of_foo = - if function_exported?(service(), :foo, 0) do - service().foo() - else - "#{inspect(service())} does not support foo" - end + value_of_foo = service().foo() {:noreply, value_of_foo} end diff --git a/test/support/mock_service.ex b/test/support/mock_service.ex deleted file mode 100644 index ec7ee1b..0000000 --- a/test/support/mock_service.ex +++ /dev/null @@ -1,11 +0,0 @@ -# With a hard coded mock like this we can define the behaviour -# before the application starts up, but do we even need Mox at -# this point? -# -# defmodule Example.MockService do -# @behaviour Example.ServiceBehaviour -# -# def foo() do -# "hard coded says foo" -# end -# end diff --git a/test/support/test1_example.ex b/test/support/test1_example.ex new file mode 100644 index 0000000..8992c0c --- /dev/null +++ b/test/support/test1_example.ex @@ -0,0 +1,7 @@ +defmodule Test1Service do + @behaviour Example.ServiceBehaviour + + def foo() do + "test 1 says foo" + end +end diff --git a/test/support/test2_example.ex b/test/support/test2_example.ex new file mode 100644 index 0000000..3a20623 --- /dev/null +++ b/test/support/test2_example.ex @@ -0,0 +1,7 @@ +defmodule Test2Service do + @behaviour Example.ServiceBehaviour + + def foo() do + "test 2 says moo" + end +end diff --git a/test/worker_test.exs b/test/worker_test.exs index b734bfd..073b61a 100644 --- a/test/worker_test.exs +++ b/test/worker_test.exs @@ -1,33 +1,30 @@ defmodule Example.WorkerTest do use ExUnit.Case - import Mox alias Example.Worker - describe "default service" do - test "returns default service foo" do - assert Worker.get_foo() =~ ~s(default says foo) - end + # In order to have the Worker use the test services we need to restart + # the process; killing it will suffice as it is a supervised process + def restart_worker() do + pid = Process.whereis(Worker) + Process.exit(pid, :kill) + Process.sleep(10) end describe "mocked service" do - setup do - # Normally you would add this to `test_helper.ex`, or `support/mocks.ex - Mox.defmock(Example.MockService, for: Example.ServiceBehaviour) + test "returns mocked service foo" do + Application.put_env(:example, :service, Test1Service) - Example.MockService - |> expect(:foo, fn -> "setup all says foo" end) + restart_worker() - :ok + assert Worker.get_foo() =~ ~s(test 1 says foo) end - setup :verify_on_exit! + test "returns mocked service moo" do + Application.put_env(:example, :service, Test2Service) - test "returns mocked service foo" do - Example.MockService - |> expect(:foo, fn -> "mock says foo" end) - |> allow(self(), Process.whereis(Worker)) + restart_worker() - assert Worker.get_foo() =~ ~s(mock says foo) + assert Worker.get_foo() =~ ~s(test 2 says moo) end end end