TestUnit - 使用 Ruby 编写测试代码 (1/3)
迷你系列
学习的序幕
我正在培训我的下属如何编写测试代码,因为我们需要提高 ExamPro 的代码覆盖率。上周日,我快速整理了关于 TestUnit、MiniTest 和 RSpec 的三份指南。为什么是这三份?我相信,通过变化学习,才能获得完整的知识。
我认为我应该“按原样”在 DEV.to 文章上发布这三篇文章,否则我会忘记它们,它们最终会被放到我的“转储”文件夹中,再也不会被看到。
附言:我之前针对每篇指南都写过挑战。不知道大家是否感兴趣。请告诉我大家的反馈,如果感兴趣的话,请在下方留言。如果有人感兴趣的话,我会找时间发布。
测试单元
TestUnit 是一个标准的 Ruby 库。
有了标准的 Ruby 库,您无需安装 gem,因为它已经包含在语言中了。
使用TestUnit最简单的例子如下。
require "test/unit/assertions"
include Test::Unit::Assertions
hello = 'world'
assert_equal 'world', hello, "hello function should return 'world'"
断言函数
assert_equal
是众多断言函数之一。
所有测试库都包含断言函数,用于断言
某些操作是否通过。断言就是你的“测试”。
以下是 TestUnit 中所有断言函数的列表。
实际应用中,你会用到其中的一些断言函数。
assert
assert_block
assert_boolean
assert_compare
assert_const_defined
assert_empty
assert_equal
assert_fail_assertion
assert_false
assert_in_delta
assert_in_epsilon
assert_include
assert_instance_of
assert_kind_of
assert_match
assert_nil
assert_no_match
assert_not_const_defined
assert_not_empty
assert_not_equal
assert_not_in_delta
assert_not_in_epsilon
assert_not_include
assert_not_match
assert_not_nil
assert_not_predicate
assert_not_respond_to
assert_not_same
assert_not_send
assert_nothing_raised
assert_nothing_thrown
assert_operator
assert_path_exist
assert_path_not_exist
assert_predicate
assert_raise
assert_raise_kind_of
assert_raise_message
assert_raises
assert_respond_to
assert_same
assert_send
assert_throw
assert_throws
assert_true
build_message
flunk
断言与失败
最基本的断言函数是assert
和flunk
。
函数签名定义了函数的输入和输出。
您可以在您使用的语言和库的技术文档中找到函数签名。让我们看一下 'assert 和
的函数签名,它们在RubyDocs中定义。flunk
断言
这是函数签名assert
#assert(boolean, message = nil) ⇒ Object
这里我们可以看到assert
输入需要两个参数:
- boolean - 必填
- 消息 - 可选
并输出一个Object
。
这就是我们使用这个函数的方法
# simple.rb
require "test/unit/assertions"
include Test::Unit::Assertions
x = true
assert x, "x should pass"
不及格
这是函数签名flunk
#flunk(message = "Flunked") ⇒ Object
这里我们可以看到flunk
输入需要一个参数:
- 消息 - 如果没有提供值,则默认为“Flunked”
并输出一个Object
。
这就是我们使用这个函数的方法
# flunk.rb
require "test/unit/assertions"
include Test::Unit::Assertions
flunk "throw a failure message"
的目的flunk
是当你的测试总是失败时。
你可能找不到实际的效用flunk
如何用 TestUnit 正确编写测试
上面的示例是使用 TestUnit 的最少代码行数,但这不是正确的方法。
正确的方法是创建一个单独的文件。
这个新的测试文件将包含我们想要测试的代码,并且我们需要创建一个 TestCase 类来编写测试函数。
Hello World 测试用例
我们将在其自己的文件中创建一个名为的简单类hello.rb
。
该类将有一个名为的类方法,self.hello
该方法将返回一个字符串。
# hello.rb
class Hello
def self.world
'world2'
end
end
我们将创建一个名为 的新文件。hello_test.rb
请注意,我们为文件名赋予了确切的名称,并将_test
其附加到末尾。
稍后你会发现,当你有更多测试文件时,由于自动化工具的原因,你需要遵循这个惯例。
# hello_test.rb
require "test/unit"
require_relative './hello'
class HelloTest < Test::Unit::TestCase
def test_world
assert_equal 'world', Hello.world, "Hello.world should return a string called 'world'"
end
def test_flunk
flunk "You shall not pass"
end
end
我们定义的类并非普通的类,而是一种领域特定语言 (DSL)。DSL
是指将代码的行为改变为语言中的语言。Ruby
语言是一种可塑性极强的语言,因此编写 DSL 非常容易。Ruby
社区在提到 DSL 时,经常说代码是“魔法”。DSL
的规则并不总是清晰的,这很容易导致混淆。
该类HelloTest
的扩展Test::Unit::TestCase
,正是将这个类变成 DSL 的“魔法”。
例如,当执行此文件时。
ruby hello_test.rb
它将输出测试结果。
它将执行HelloTest
类中以 开头的每个实例函数test_
- 测试世界
- 测试_flunk
如果实例函数没有使用 来命名,test_
那么它将无法运行。
这是该 DSL 规则的一部分。
关于 TestUnit 的思考
TestUnit
并不是 Ruby 中唯一的测试框架,因为我们还有MiniTest
和Rspec
。
这些其他测试框架各有优缺点,但功能相似。
TestUnit
是古老而简单的测试框架。
在接下来的讲座中,我们将探讨这两者MiniTest
并了解Rspec
其
权衡之处。
所有框架都会被使用,很难说哪个最常用
,这取决于团队的偏好。
代码
# flunk.rb | |
require "test/unit/assertions" | |
include Test::Unit::Assertions | |
flunk "throw a failure message" |
# flunk.rb | |
require "test/unit/assertions" | |
include Test::Unit::Assertions | |
flunk "throw a failure message" |
# flunk.rb | |
require "test/unit/assertions" | |
include Test::Unit::Assertions | |
flunk "throw a failure message" |
class Hello | |
def self.world | |
'world2' | |
end | |
end |
class Hello | |
def self.world | |
'world2' | |
end | |
end |
class Hello | |
def self.world | |
'world2' | |
end | |
end |
require "test/unit" | |
require_relative './hello' | |
class HelloTest < Test::Unit::TestCase | |
def test_world | |
assert_equal 'world', Hello.world, "Hello.world should return a string called 'world'" | |
end | |
def test_flunk | |
flunk "You shall not pass" | |
end | |
end |
require "test/unit" | |
require_relative './hello' | |
class HelloTest < Test::Unit::TestCase | |
def test_world | |
assert_equal 'world', Hello.world, "Hello.world should return a string called 'world'" | |
end | |
def test_flunk | |
flunk "You shall not pass" | |
end | |
end |
require "test/unit" | |
require_relative './hello' | |
class HelloTest < Test::Unit::TestCase | |
def test_world | |
assert_equal 'world', Hello.world, "Hello.world should return a string called 'world'" | |
end | |
def test_flunk | |
flunk "You shall not pass" | |
end | |
end |
require "test/unit/assertions" | |
include Test::Unit::Assertions | |
x = true | |
assert x, "x should pass" |
require "test/unit/assertions" | |
include Test::Unit::Assertions | |
x = true | |
assert x, "x should pass" |
require "test/unit/assertions" | |
include Test::Unit::Assertions | |
x = true | |
assert x, "x should pass" |
参考
https://stackoverflow.com/questions/12317921/why-undefined-method-assert-equal-is-thrown-even-after-requiring-test-unit
https://apidock.com/ruby/Test/Unit/Assertions
https://www.rubydoc.info/gems/test-unit/2.3.0/Test/Unit/Assertions
https://stackoverflow.com/questions/6515333/how-do-i-execute-a-single-test-using-ruby-test-unit
https://mattbrictson.com/minitest-and-rails