Emily
Emily

Reputation: 2331

How To Use Constants in Elixir Tests?

I'm defining a constant in my main module this way:

@end_digits_adjusters [11, 12, 14, 21, 22, 23] 

Here's how I'm trying to test for it:

defmodule PriceOptimizerTest do
  use ExUnit.Case
  doctest PriceOptimizer

  test "get_random_adjuster() randomizer" do
    adj_num = PriceOptimizer.get_random_adjuster()
    is_in? = adj_num in @end_digits_adjusters
    assert is_in? == true

  end
end

That doesn't work. But when I explicitly specify the constant value in the test it does work. Like this...

is_in? = adj_num in [11, 12, 14, 21, 22, 23]

Am I missing a step somewhere to get Elixir to recognize the module constants in the tests?

Upvotes: 2

Views: 1302

Answers (2)

Steve Pallen
Steve Pallen

Reputation: 4507

Shared constants, which are popular in other languages, is not a popular pattern in Elixir. I have personally found that most of the time I don't need to use the pattern. But there are some applications I've built relay heavily on them.

When I do need them, I have a constants module I use the following module:

defmodule Constants do
  @moduledoc """
  An alternative to use @constant_name value approach to defined reusable
  constants in elixir.

  This module offers an approach to define these in a
  module that can be shared with other modules. They are implemented with
  macros so they can be used in guards and matches

  ## Examples:

  Create a module to define your shared constants

      defmodule MyConstants do
        use Constants

        define something,   10
        define another,     20
      end

  Use the constants

      defmodule MyModule do
        require MyConstants
        alias MyConstants, as: Const

        def myfunc(item) when item == Const.something, do: Const.something + 5
        def myfunc(item) when item == Const.another, do: Const.another
      end

  """

 defmacro __using__(_opts) do
    quote do
      import Constants
    end
  end

  @doc "Define a constant"
  defmacro constant(name, value) do
    quote do
      defmacro unquote(name), do: unquote(value)
    end
  end

  @doc "Define a constant. An alias for constant"
  defmacro define(name, value) do
    quote do
      constant unquote(name), unquote(value)
    end
  end

  @doc """
    Import an hrl file.

    Create constants for each -define(NAME, value).
  """
  defmacro import_hrl(file_name) do
    list = parse_file file_name
    quote bind_quoted: [list: list] do
      for {name, value} <- list do
        defmacro unquote(name)(), do: unquote(value)
      end
    end
  end

  defp parse_file(file_name) do
    for line <- File.stream!(file_name, [], :line) do
      parse_line line
    end
    |> Enum.filter(&(not is_nil(&1)))
  end

  defp parse_line(line) do
    case Regex.run ~r/-define\((.+),(.+)\)\./, line do
      nil -> nil
      [_, name, value] ->
        {String.strip(name) |> String.downcase |> String.to_atom, String.strip(value) |> parse_value}
      _ -> nil
    end
  end

  defp parse_value(string) do
    case Integer.parse string do
      :error -> filter_string(string)
      {num, _} -> num
    end
  end

  defp filter_string(string), do: String.replace(string, "\"", "")
end

Couple notes:

  • They work in pattern matching!
  • Don't import your constants module. Its hard to find where they are defined
  • Alias the module for brevity `alias MyConstantsMod, as: Const
  • This module also supports working with Erlang hrl files
  • pay attention to the require statements. The define is a macro.
  • Converting a list of C #define statements is only a few key strokes when using sublime's multi cursor and vi key bindings :)

Upvotes: 5

Paweł Dawczak
Paweł Dawczak

Reputation: 9639

I'm afraid, Elixir's module attributes are available only within the module where are defined. This is due fact that those attributes are online in code during compilation phase.

If you want to make them publicly accessible, you need to wrap it with function, eg:

defmodule MyMod do
  @test "hello"

  def test, do: @test
end

So:

MyMod.test # => "hello"

Hope that helps!

Upvotes: 1

Related Questions