在 Elixir 赛道上测试

了解如何在 Exercism 上测试你的 Elixir 练习


在终端中,先切换到这个练习的根目录,然后用下面的命令运行测试:

$ mix test

这会运行test子文件夹中的测试文件,也就是以_test.exs结尾的文件。

待处理测试

在练习的测试套件中,除了第一个测试之外,其余测试都被标记为跳过。

当某个测试通过后,你可以用#号注释掉相应的@tag :pending,从而取消跳过下一个测试。

例如:

# @tag :pending
test "shouting" do
  assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end

如果你想一次运行所有测试,可以在mix test命令上使用--include标志,把所有被跳过的测试也包含进来:

$ mix test --include pending

或者,你也可以注释掉测试套件里的ExUnit.configure那一行,来启用所有测试。

# ExUnit.configure exclude: :pending, trace: true

其他 Elixir 测试功能

ExUnit和mix test提供了不少对测试进行分组、打标签和执行的方法,以及各种控制测试执行的方式,下面概述了其中的大部分。

运行特定测试的方法

文档:

运行某个文件中的测试

指定文件后,就可以用mix test运行单个文件中的所有测试:

$ mix test test/<FILE>.exs

注意:tagging可能会影响这种方式实际运行的测试。

运行单个测试

通过引用测试在文件中的行号,可以运行单个测试:

$ mix test test/<FILE>.exs:LINENUM

用:分隔多个行号,就可以运行多个测试。

例如,给定一个带有行号、内容如下的文件:

test "Test 1" do           # 1
  # test implementation    # 2-6
end                        # 7
                           # 8
test "Test 2" do           # 9
  # test implementation    # 10-21
end                        # 22
                           # 23
test "Test 3" do           # 24
  # test implementation    # 25-35
end                        # 36

第 1 个和第 3 个测试可以这样运行:

$ mix test test/FILE.exs:1:24

注意:通过行号指定测试时,tagging会被忽略。

运行测试组

可以用describe对测试进行分组:

describe "short test group description" do
  test "test description" do
    # test implementation
  end

  test "another test description" do
    # test implementation
  end
end

只要引用测试组在文件中的行号,就可以运行组内的所有测试,和引用、运行单个测试一样。

文档:

其他有用的mix test选项

  • --include 和 --exclude:根据测试的@tag决定运行或不运行特定测试
  • --failed:只运行上一次运行时失败的测试
  • --max-failures:当测试失败数量达到这个数字时,测试套件就停止运行测试
  • --seed:为用来随机化测试顺序的随机数生成器设置种子,--seed 0会禁用随机化,这样单个文件里的测试就总是按定义时的顺序运行
  • --stale:只运行那些引用的模块自上次用--stale运行测试以来发生变化的测试
  • --only task_id:1:换成别的编号也一样,在学习练习中只运行与这个特定任务相关联的测试

文档:

类型规格与 Dialyzer(DIscrepancy AnalYZer for ERlang programs)

Elixir 练习在lib子目录中包含一个骨架实现文件。这个文件列出了你需要实现的模块和函数。在大多数练习中,你会在函数声明上方看到类型规格。这些类型规格以@spec标签开头,通常遵循@spec function_name(type1, type2) :: return_type这样的格式。在 Elixir 和 Erlang 中,它们既用作文档,也和名为 Dialyzer 的工具配合使用,来发现类型不一致和可能的 bug。更多信息请参阅类型规格文档。关于 Dialyzer 的文档,请参阅Erlang -- dialyzer。

你也可以选择用 Dialyzer 检查自己实现的类型。你需要完成几个步骤。为此,你必须在题目的mix.exs文件中添加Dialyxir依赖。

defp deps do
  # Add this:
  [{:dialyxir, "~> 0.4", only: [:dev]}]
end

然后用 mix 任务在命令行上获取并编译:

$ mix deps.get
...
$ mix deps.compile
...

如果你是第一次运行 Dialyzer,很可能还没有plt文件。Dialyzer 使用持久查找表(persistent lookup table,简称 PLT)来缓存内置 Elixir 和 Erlang 类型的信息。要用合理的默认值创建 plt,请运行:

$ mix dialyzer --plt

最后,可以这样运行:

$ mix dialyzer

请务必把这里的路径改成你系统中 Elixir 库的路径。例如,如果你用 homebrew 安装了 Elixir,它很可能在/usr/local/Cellar/elixir/1.3.2下面。

需要再次强调,完成练习时运行 Dialyzer 并消除所有警告是一个可选的步骤。Dialyzer 的警告有时很难解读。例如,看看这个Bob练习中一个非常愚蠢的实现所产生的警告。

defmodule Bob do
  @spec hey(input :: String.t()) :: String.t()
  def hey(input) do
    1
  end

  def hey(input) do
  end
end

这会产生以下警告。

bob.exs:2: Invalid type specification for function 'Elixir.Bob':hey/1. The success typing is (_) -> 1
bob.exs:7: The variable _input@1 can never match since previous clauses completely covered the type any()

第一条警告表示这个函数返回的类型不正确。最后一条说明第二个函数定义永远无法被执行到,因为第一个函数定义总是匹配。