【问题标题】:How To Use Constants in Elixir Tests?如何在 Elixir 测试中使用常量?
【发布时间】:2017-04-22 23:39:20
【问题描述】:

我这样在我的主模块中定义一个常量:

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

这是我尝试测试的方式:

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

这行不通。但是当我在测试中明确指定常量值时,它确实有效。像这样……

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

我是否错过了让 Elixir 识别测试中的模块常量的步骤?

【问题讨论】:

    标签: elixir


    【解决方案1】:

    共享常量在其他语言中很流行,但在 Elixir 中并不流行。我个人发现,大多数时候我不需要使用该模式。但是有一些应用程序我已经在它们上大量构建了。

    当我确实需要它们时,我有一个常量模块,我使用以下模块:

    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
    

    情侣笔记:

    • 他们在模式匹配中工作!
    • 不要导入常量模块。很难找到它们的定义位置
    • 为简洁的模块起别名 `alias MyConstantsMod, as: Const
    • 此模块还支持处理 Erlang hrl 文件
    • 注意require 语句。 define 是一个宏。
    • 当使用 sublime 的多光标和 vi 键绑定时,转换 C #define 语句列表只需几个键击:)

    【讨论】:

      【解决方案2】:

      恐怕Elixir's module attributes 仅在定义的模块中可用。这是因为这些属性在编译阶段在代码中是在线的。

      如果你想让它们公开访问,你需要用函数包装它,例如:

      defmodule MyMod do
        @test "hello"
      
        def test, do: @test
      end
      

      所以:

      MyMod.test # => "hello"
      

      希望有帮助!

      【讨论】:

      • 是否有一种优雅的解决方法来构建使用常量的测试,而不必将常量包装在模块中?
      • @Emily 不行,别无他法。
      猜你喜欢
      • 1970-01-01
      • 2021-03-28
      • 2019-01-08
      • 1970-01-01
      • 2019-08-01
      • 2016-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多