Getting undefined function error - but it is present

I’m getting a function undefined error that totally mystifies me:

  describe "sessions" do
    alias Koko.Authentication.Session

    @valid_user_attrs %{"admin" => true, "blurb" => "BLURB!", "email" => "yada@foo.io",
    "name" => "Yada T. Urdik", "username" => "yada", "password" => "abc.617.ioj"}

    def user_fixture2(attrs \\ %{}) do
      {:ok, user} =
        attrs
        |> Enum.into(@valid_user_attrs)
        |> Authentication.create_user()
      user
    end

    user = user_fixture2(%{})
   #ETC.

Then

** (CompileError) apps/koko/test/koko/authentication/authentication_test.exs:94: 
undefined function user_fixture2/1

If you move the function definition outside of the describe/2 block, it will be available. You may want to check the documentation for describe/2, specifically the section on hierarchies, it may fit your needs.

I think there is probably a debate on whether or not what you are trying to do should be allowed.

I tried that – same error.

I have two describe blocks. They both need to use user_fixture. I moved that definition out of the first block, deleted the definition of user_fixture2, and now have two calls to ``user_fixture`: on in the first describe block, the other in the second. The error comes in the second block.

Do you have a repository with a small example to show this behaviour? Because after doing a small test, defining the function in the describe/2 block works as expected.

You could also try using an anonymous function instead of a named one.

I have koko_dev and koko_test repositories. The tests should be addressing the former – it is empty.

I’m sorry, I meant a git repository or something. Somewhere we can take a look at the code.

Yes, I just pushed the branch I am working on:

Alright. I am not entirely sure the reason behind it, but it turns out you cannot call the function in the describe/2 block, but you can call it in the test/1 block. I am not really sure of the limitations on this because I was able to call List.first/1 without an issue. So maybe it is limited to functions defined in the same exs file?

Either way, what you should be doing is using a setup_all/1 block in order to do something for all of the tests within the describe/2.

2 Likes

You can’t call functions from within the module body that defines them.

3 Likes

So being within the describe/2 block is considered inside of the module body, but being inside the test/1 block does not?

Yup. You can tell because it lets you set module attributes in the describe block, which you can’t do inside of a function.

1 Like

There’s a better way to explain what I’m about to say, but you’re trying to assign a variable in the AST using a method that’s also a leaf in the same tree.

You will have to declare the method in a new module entirely or use it in a test or setup block of that module.

2 Likes

An example, they are saying that you are trying to do something like this:

iex(2)> defmodule Testering do 
...(2)>   def blah(i), do: i * 2
...(2)>   bloop = blah(21)
...(2)> end
** (CompileError) iex:4: undefined function blah/1

You are trying to use a function that is not compiled yet is all. :slight_smile:

The ‘test’ cases are basically (are, not just basically) functions, hence why you can call it in those.

2 Likes

Thanks! setup_all did the trick … it needed code as indicated below:

  setup_all do
    # Explicitly get a connection before each test
    # By default the test is wrapped in a transaction
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Koko.Repo)

    # The :shared mode allows a process to share
    # its connection with any other process automatically
    Ecto.Adapters.SQL.Sandbox.mode(Koko.Repo, { :shared, self() })

    :ok
  end

Thanks!

Aha! (duh on my part) :smile:

Aha! that explains it!

1 Like