We have the following test at the beginning of the chapter
defmodule InfoSysTest.CacheTest do
use ExUnit.Case, async: true
alias InfoSys.Cache
@moduletag clear_interval: 100
setup %{test: name, clear_interval: clear_interval} do
{:ok, pid} = Cache.start_link(name: name, clear_interval: clear_interval)
{:ok, name: name, pid: pid}
end
Where is name
supposed to be coming from? This obviously makes the test fail
It is the test context. Here is the relevant document
Hey thanks for the link. I just read the section and couldn’t find an answer
The chapter adds the following tests:
test "key value pairs can be put and fetched from cache", %{name: name} do
assert :ok = Cache.put(name, :key1, :value1)
assert :ok = Cache.put(name, :key2, :value2)
assert Cache.fetch(name, :key1) == {:ok, :value1}
assert Cache.fetch(name, :key2) == {:ok, :value2}
end
test "key value pairs can be put and fetched from cache", %{name: name} do
assert :ok = Cache.put(name, :key1, :value1)
assert :ok = Cache.put(name, :key2, :value2)
assert Cache.fetch(name, :key1) == {:ok, :value1}
assert Cache.fetch(name, :key2) == {:ok, :value2}
end
test "unfound entry returns error", %{name: name} do
assert Cache.fetch(name, :notexists) == :error
end
Which fails with:
warning: variable "name" does not exist and is being expanded to "name()", please use parentheses to remove the ambiguity or change the variable name
test/cache_test.exs:12: InfoSysTest.CacheTest."test key value pairs can be put and fetched from cache"/1
warning: variable "name" does not exist and is being expanded to "name()", please use parentheses to remove the ambiguity or change the variable name
test/cache_test.exs:13: InfoSysTest.CacheTest."test key value pairs can be put and fetched from cache"/1
warning: variable "name" does not exist and is being expanded to "name()", please use parentheses to remove the ambiguity or change the variable name
test/cache_test.exs:15: InfoSysTest.CacheTest."test key value pairs can be put and fetched from cache"/1
warning: variable "name" does not exist and is being expanded to "name()", please use parentheses to remove the ambiguity or change the variable name
test/cache_test.exs:16: InfoSysTest.CacheTest."test key value pairs can be put and fetched from cache"/1
warning: undefined function name/0
test/cache_test.exs:16
warning: undefined function name/0
test/cache_test.exs:15
warning: undefined function name/0
test/cache_test.exs:13
== Compilation error in file test/cache_test.exs ==
** (CompileError) test/cache_test.exs:12: undefined function name/0
(elixir 1.11.3) src/elixir_locals.erl:114: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
(stdlib 3.14) erl_eval.erl:680: :erl_eval.do_apply/6
(elixir 1.11.3) lib/kernel/parallel_compiler.ex:416: Kernel.ParallelCompiler.require_file/2
(elixir 1.11.3) lib/kernel/parallel_compiler.ex:316: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7
Any ideas ?
%{test: name}
, not %{name: name}
Please read this doc
Sorry, it looks like the setup was trying to merge in [name: name, pid: pid]
to the context, so that should work. did you add a differrent setup clause so the setup you cited at the beginning did not run?
Silly me forgot to add %{name: name}
in one of the test headers
However I did just paste the whole thing and now am getting following errors:
1) test unfound entry returns error (InfoSysTest.CacheTest)
test/cache_test.exs:36
** (MatchError) no match of right hand side value: {:error, {:already_started, #PID<0.178.0>}}
stacktrace:
test/cache_test.exs:24: InfoSysTest.CacheTest.__ex_unit_setup_0/1
test/cache_test.exs:1: InfoSysTest.CacheTest.__ex_unit__/2
2) test key value pairs can be put and fetched from cache (InfoSysTest.CacheTest)
test/cache_test.exs:28
** (MatchError) no match of right hand side value: {:error, {:already_started, #PID<0.178.0>}}
stacktrace:
test/cache_test.exs:24: InfoSysTest.CacheTest.__ex_unit_setup_0/1
test/cache_test.exs:1: InfoSysTest.CacheTest.__ex_unit__/2
Finished in 0.06 seconds
2 tests, 2 failures
Randomized with seed 790950
It is a good exercise, please try to figure it out on your own. You can do it.
2 Likes
Thanks for the encouragement and sorry to disappoint but I’ve been trying since and haven’t succeeded. Here is my thought process so far
If the process is already started, then something must be starting it twice with the same name.
So I stick a simple debug line in the start_link
method to see how many times it gets called, and sure enough it only gets called once.
I figure maybe it just wasn’t properly exited from a previous test, but then again iex
isn’t running nor are the tests and if I’m not mistaken in both cases the GenServer would be linked to the calling process (either iex or test context), and thus would be exited.
So the issue is most likely in the test definition itself, however start_link
definitely only happens once, since the IO.puts
I inserted in it only prints once
Gonna keep at it but not sure where to go from here
I also still do not understand where the name
in setup
is coming from. I get that setup runs before every test and that if there are tags above said tests that they will be injected into setup, but this is not the case
name
in the setup is coming from the matched :test
key, which is put into the context by exUnit by default to the name of the test.
setup
is ran per test. Do you have 2 tests by the same name? Do you know if the Cache
module pass the name down to registration? Processes shall be registered using atoms not strings. So I have doubt.
1 Like
This was it. I had:
def start_link(opts) do
opts = Keyword.put_new(opts, :name, __MODULE__)
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
I miscopied, in the book is written:
def start_link(opts) do
opts = Keyword.put_new(opts, :name, __MODULE__)
GenServer.start_link(__MODULE__, opts, name: opts[:name])
end
The tests now pass, but this leads to me another question:
How is opts[:name]
in this case not always the same, i.e., opts[:name] == __MODULE__
?
So I logged opts[:name]
as follows:
def start_link(opts) do
opts = Keyword.put_new(opts, :name, __MODULE__)
IO.inspect(opts, label: "OPTS")
GenServer.start_link(__MODULE__, opts, name: opts[:name])
end
And I get:
OPTS: [name: InfoSys.Cache]
OPTS: [
name: :“test key value pairs can be put and fetched from cache”,
clear_interval: 100
]
.OPTS: [name: :“test unfound entry returns error”, clear_interval: 100]
I thought __MODULE__
was always the name of the module in which it’s written. Does this mean that the value of __MODULE__
is actually the value of module calling it?
Keyword.put_new/3
will only put when the key was not there already. In your case, the opts is coming from the setup, which put in a :name
key already.
I learned something too; it looks like you can register a process under a string, instead of the usual atom. I still don’t think this is a good practice though.
1 Like
Makes sense.
OPTS: [
name: :“test key value pairs can be put and fetched from cache”,
clear_interval: 100
]
.OPTS: [name: :“test unfound entry returns error”, clear_interval: 100]
pretty sure these are still atoms, no?
You are right, it is still atom, just long and weird looking.