ook
Load module during Test
I’m writting tests for a simple module which do HTTP requests.
As recommanded in Mocks and explicit contracts « Plataformatec Blog I use an environnement variable instead of mocking:
defmodule FaLogentriesParserEx do
@logentries_api Application.get_env(:fa_logentries_parser_ex, :logentries_api)
end
Of course, for dev and prod environment, the module to load in under lib/fa_logentries_parser_ex but since I don’t want test stuff under lib/ I created the “mock” module under test/fa_logentries_parser_ex/logentries_api_fake.exs
My problem: mix test can’t find that module:
1) test handle_pipeline_status (FaLogentriesParserExTest)
test/fa_logentries_parser_ex_test.exs:9
** (UndefinedFunctionError) function FaLogentriesParserExTest.LogEntriesApiFake.fetch/2 is undefined (module FaLogentriesParserExTest.LogEntriesApiFake is not available)
stacktrace:
FaLogentriesParserExTest.LogEntriesApiFake.fetch(1501576495019, 1501576555019)
(fa_logentries_parser_ex) lib/fa_logentries_parser_ex.ex:10: FaLogentriesParserEx.fetch/1
test/fa_logentries_parser_ex_test.exs:89: (test)
Could you help me to make that module loadable or advice a better way to test?
Marked As Solved
peerreynders
Here is the way I did it with a behaviour:
1.) In the mix.exs use elixirc_paths:
def project do
[app: :coffee_fsm,
version: "0.1.0",
elixir: "~> 1.4",
elixirc_paths: elixirc_paths(Mix.env),
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps()]
end
# Configuration for the OTP application
#
# Type "mix help compile.app" for more information
def application do
# Specify extra applications you'll use from Erlang/Elixir
[extra_applications: [:logger]]
end
defp elixirc_paths(:test), do: ["lib","test/support"]
defp elixirc_paths(_), do: ["lib"]
essentially this adds the “test/support” directory where the mock files are
2). in config/config.exs:
case Mix.env do
:test ->
config :coffee_fsm, hw: HwMock
_ ->
config :coffee_fsm, hw: HwOutput
end
Essentially this will cause test\support\hw_mock.ex to load for testing through @hw_impl in lib\hw.ex - otherwise lib\hw_output.ex is loaded.
3). Meanwhile lib\hw.ex looks like this:
defmodule Hw do
@hw_impl Application.fetch_env!(:coffee_fsm, :hw)
defmodule Behaviour do
@callback display(f,a) :: :ok when f: String.t(), a: [any()]
@callback return_change(n) :: :ok when n: non_neg_integer()
@callback drop_cup() :: :ok
@callback prepare(t) :: :ok when t: atom()
@callback reboot() :: :ok
end
@spec display(s,a) :: :ok when s: String.t(), a: [any()]
def display(str, args), do: @hw_impl.display(str, args)
@spec return_change(n) :: :ok when n: non_neg_integer()
def return_change(payment), do: @hw_impl.return_change(payment)
@spec drop_cup :: :ok
def drop_cup, do: @hw_impl.drop_cup()
@spec return_change(b) :: :ok when b: CoffeeFsm.beverage()
def prepare(type), do: @hw_impl.prepare(type)
@spec reboot :: :ok
def reboot, do: @hw_impl.reboot()
end
while test\support\hw_mock.ex looks like this:
defmodule HwMock do
@behaviour Hw.Behaviour
use GenServer
defp forward_pending(pending, pid) when is_pid pid do
forward_to =
fn(request, _) ->
Kernel.send pid, request
:ok
end
pending
|> Enum.reverse()
|> Enum.reduce(:ok, forward_to)
end
def handle_cast(entry, {:none, pending}) do
{:noreply, {:none, [entry | pending]}}
end
def handle_cast(entry, {pid, pending}) do
forward_pending [entry | pending], pid
{:noreply, {pid, []}}
end
def handle_call({:forward, pid}, _from, {_, pending}) when is_pid pid do
forward_pending pending, pid
{:reply, :ok, {pid, []} }
end
def handle_call({:forward, _}, _from, {_, pending}) do
{:reply, :ok, {:none, pending} }
end
def handle_call(:clear, _from, {pid, _}) do
{:reply, :ok, {pid, []} }
end
defp log(entry) do
GenServer.cast __MODULE__, entry
end
def init(_args), do: {:ok, {:none, []}}
# public interface
def start_link, do: GenServer.start_link __MODULE__, [], name: __MODULE__
def clear(pid) do
GenServer.call __MODULE__, :clear
end
def forward(pid) do
GenServer.call __MODULE__, {:forward, pid}
end
# implementing "Hw"" behaviour
def display(str, args) do
log { :display, [str, args] }
:ok
end
def return_change(payment) do
log { :return_change, [payment] }
:ok
end
def drop_cup do
log { :drop_cup, [] }
:ok
end
def prepare(type) do
log { :prepare, [type] }
:ok
end
def reboot do
log { :reboot, [] }
:ok
end
end
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









