测试 Crystal 代码¶
Crystal 在 Spec
模块 中附带了一个功能齐全的规范库。它为编写代码行为的可执行示例提供了一种结构。
它受到 Rspec 的启发,包含一个领域特定语言 (DSL),允许您以类似于普通英语的方式编写示例。
一个基本的规范看起来像这样
require "spec"
describe Array do
describe "#size" do
it "correctly reports the number of elements in the Array" do
[1, 2, 3].size.should eq 3
end
end
describe "#empty?" do
it "is true when no elements are in the array" do
([] of Int32).empty?.should be_true
end
it "is false if there are elements in the array" do
[1].empty?.should be_false
end
end
end
规范文件剖析¶
要使用规范模块和 DSL,您需要在规范文件中添加 require "spec"
。许多项目使用自定义的 规范助手 来组织这些包含的内容。
具体的测试用例在 it
块中定义。一个可选的(但强烈推荐的)描述性字符串说明了它的目的,一个块包含执行测试的主要逻辑。
已经定义或概述但尚未预期工作 的测试用例可以使用 pending
而不是 it
来定义。它们不会被运行,但会在规范报告中显示为挂起。
一个 it
块包含一个应该调用要测试的代码并定义对其期望的示例。每个示例可以包含多个期望,但它应该只测试一个特定的行为。
当包含 spec
时,每个对象都有实例方法 #should
和 #should_not
。这些方法在要测试的值上调用,并带有一个期望作为参数。如果满足期望,代码执行将继续。否则,示例已失败,此块中的其他代码将不会被执行。
在测试文件中,规范由示例组结构化,这些组由 describe
和 context
部分定义。通常,顶级 describe
定义规范要测试的外部单元(例如类)。更进一步的 describe
部分可以嵌套在外部单元中,以指定要测试的更小的单元(例如单个方法)。
对于单元测试,建议遵循方法命名约定:外部 describe
是类的名称,内部 describe
目标是方法。实例方法以 #
为前缀,类方法以 .
为前缀。
要建立某些上下文 - 考虑空数组与包含元素的数组 - context
方法可用于向读者传达这一点。它具有不同的名称,但行为与 describe
完全相同。
describe
和 context
接受一个描述作为参数(通常应该是一个字符串)和一个包含单个规范或嵌套分组的块。
期望¶
期望定义要测试的值(实际)是否与某个值或特定条件匹配。
等价性、标识和类型¶
有一些方法可以创建期望,这些期望测试等价性 (eq
)、标识 (be
)、类型 (be_a
) 和 nil (be_nil
)。请注意,标识期望使用 .same?
,它测试 #object_id
是否相同。这只有在预期值指向同一个对象而不是一个等效的对象时才为真。这仅对引用类型有效,对结构或数字等值类型无效。
actual.should eq(expected) # passes if actual == expected
actual.should be(expected) # passes if actual.same?(expected)
actual.should be_a(expected) # passes if actual.is_a?(expected)
actual.should be_nil # passes if actual.nil?
真值¶
actual.should be_true # passes if actual == true
actual.should be_false # passes if actual == false
actual.should be_truthy # passes if actual is truthy (neither nil nor false nor Pointer.null)
actual.should be_falsey # passes if actual is falsey (nil, false or Pointer.null)
比较¶
actual.should be < expected # passes if actual < expected
actual.should be <= expected # passes if actual <= expected
actual.should be > expected # passes if actual > expected
actual.should be >= expected # passes if actual >= expected
其他匹配器¶
actual.should be_close(expected, delta) # passes if actual is within delta of expected:
# (actual - expected).abs <= delta
actual.should contain(expected) # passes if actual.includes?(expected)
actual.should match(expected) # passes if actual =~ expected
期望错误¶
这些匹配器运行一个块,如果它引发了某个异常,则通过。
expect_raises(MyError) do
# Passes if this block raises an exception of type MyError.
end
expect_raises(MyError, "error message") do
# Passes if this block raises an exception of type MyError
# and the error message contains "error message".
end
expect_raises(MyError, /error \w{7}/) do
# Passes if this block raises an exception of type MyError
# and the error message matches the regular expression.
end
expect_raises
返回已恢复的异常,因此它可以用于进一步的期望,例如验证异常的特定属性。
ex = expect_raises(MyError) do
# Passes if this block raises an exception of type MyError.
end
ex.my_error_value.should eq "foo"
专注于一组规范¶
describe
、context
和 it
块可以使用 focus: true
标记,例如
it "adds", focus: true do
(2 + 2).should_not eq(5)
end
如果任何带有 focus: true
的内容都被标记,那么只有那些示例会被运行。
标记规范¶
标签可用于对规范进行分组,允许在向规范运行器提供 --tag
参数时仅运行规范的子集(参见 使用编译器)。
describe
、context
和 it
块可以被标记,例如
it "is slow", tags: "slow" do
sleep 60
true.should be_true
end
it "is fast", tags: "fast" do
true.should be_true
end
标记一个示例组 (describe
或 context
) 将扩展到所有包含的示例。
多个标签可以通过给出 Enumerable
来指定,例如 Array
或 Set
。
运行规范¶
Crystal 编译器有一个 spec
命令,它包含用于约束运行哪些示例并调整输出的工具。项目的所有规范都是通过命令 crystal spec
编译和执行的。
按照惯例,规范位于项目的 spec/
目录中。规范文件必须以 _spec.cr
结尾,以便编译器命令能够识别它们。
您可以从文件夹树、单个文件或文件中特定行编译和运行规范。如果指定的行是 describe
或 context
部分的开头,则该组中的所有规范都会运行。
默认格式化程序输出失败规范的文件和行样式命令,这使得很容易重新运行仅此单个规范。
您可以使用开关 --no-color
关闭颜色。
随机化规范顺序¶
规范默认情况下按定义的顺序运行,但可以通过将 --order random
传递给 crystal spec
来随机运行。
随机顺序运行的规范将在完成后显示一个种子值。此种子值可用于通过将种子值传递给 --order
来以相同顺序重新运行规范。
示例¶
# Run all specs in files matching spec/**/*_spec.cr
crystal spec
# Run all specs in files matching spec/**/*_spec.cr without colors
crystal spec --no-color
# Run all specs in files matching spec/my/test/**/*_spec.cr
crystal spec spec/my/test/
# Run all specs in spec/my/test/file_spec.cr
crystal spec spec/my/test/file_spec.cr
# Run the spec or group defined in line 14 of spec/my/test/file_spec.cr
crystal spec spec/my/test/file_spec.cr:14
# Run all specs tagged with "fast"
crystal spec --tag 'fast'
# Run all specs not tagged with "slow"
crystal spec --tag '~slow'
还有其他选项用于按名称运行规范、调整输出格式、进行试运行等,请参见 使用编译器。
规范助手¶
许多项目使用自定义规范助手文件,通常命名为 spec/spec_helper.cr
。
此文件用于需要 spec
和其他包含的内容,例如每个规范文件所需的项目中的代码。这也是定义全局助手方法的好地方,这些方法使编写规范更容易并避免代码重复。
require "spec"
require "../src/my_project.cr"
def create_test_object(name)
project = MyProject.new(option: false)
object = project.create_object(name)
object
end
require "./spec_helper"
describe "MyProject::Object" do
it "is created" do
object = create_test_object(name)
object.should_not be_nil
end
end