稀土掘金 稀土掘金

Rails中的Minitest vs. RSpec-区别介绍

Rails是一个几乎包含了所有东西的框架,专注于惯例而不是配置。 Minitest就是这些约定之一。Minitest小而快,它提供了许多断言,使测试可读而干净。

然而,有许多Minitest的替代品。最流行的一个是 RSpec。

RSpec的目标与Minitest相同,但侧重于可读的规范,描述应用程序应该如何表现,并与英语紧密结合。Minitest声称,如果你对Ruby很了解,应该就足够了。

RSpec项目专注于行为驱动开发(BDD)和规范编写。测试不仅验证了你的应用程序代码,而且还提供了详细的表达方式,解释了应用程序应该如何表现。

Minitest也支持测试驱动开发(TDD)、BDD、嘲弄和基准测试。然而,它们有细微的差别,所以我们将通过RSpec和Minitest中的一个例子应用来展示它们的差别。在我们开始之前,让我们介绍一下所有测试框架的共同点。

让我们回顾一下:什么是测试?

TDD和BDD都鼓励以测试为先的方法,这意味着你从编写测试开始。你运行测试,测试应该是失败的。然后你写代码,使测试通过。

通过编写大量的测试来覆盖我们整个程序的次要方面,我们最终应该达到一个几乎完美的系统。当然,这是软件,所以这在现实中不会发生。然而,我们可以通过许多测试对我们的软件进行大幅度的修改,减少风险。自动测试可以成为小型创业公司为寻找市场契合点而进行的成功与失败之间的区别。在一个大公司里,创新不需要放慢速度。

什么是测试?我们可以通过在软件世界中把测试分成三大类来回答这个问题。

单元测试

单元测试是由软件开发人员编码的小型自动测试,以验证一小段生产代码--一个单元--是否按预期运行。

关于单元测试的文章很多,但你应该知道一个缩写是 FIRST,它是由Tim Ottinger和Jeff Langr首先使用:

  • F - 快速 - 测试必须快速运行。他们运行得越快,你就越有可能运行它们。
  • I - 隔离 - 每个测试都应该有一个失败的原因。
  • R - 可重复的 - 测试应该在每次运行时有相同的结果。
  • S - 自我验证 - 结果不应该由人类解释。它们应该是二进制的(例如,红色/绿色)。
  • T - 及时 - 在写代码之前写测试。

集成测试

下一个大类别是集成测试。这些测试验证了小块的代码是否工作。

因此,我们可以确定A、B、C和D单元测试独立工作,但你怎么知道它们一起工作?

集成测试有时会很难写,可能是作为修复错误过程的一部分来创建。

UI测试

这个测试分支通常具有最高的计算成本,运行模拟的用户体验来测试错误、流程和其他一切。它最常被用来取代或协助专业的质量保证测试人员。

现在我们已经把软件测试分成了大的类别,让我们更进一步,在比较RSpec和Minitest之前探索另一个重要的概念。

红色、绿色和重构的生命周期

在我第一份实行TDD的开发工作中,高级开发人员使用了三个 "G"。

"让它失败,让它工作,然后让它更好"。

这使得我们的工作流程看起来像这样:

Red, Green, and Refactor Lifecycle with Honeybadger Backdrop

基本上,我们先写一个测试,然后运行它。测试会失败。接下来,我们写代码,使测试通过。测试通过后,我们就可以继续前进,改进实现。慢慢地,但肯定的是,我们到达了我们的理想实现。

这个简单的工作流程似乎有悖常理,因为它最初比较慢。然而,从长远来看,它是更快的。

比较Minitest和RSpec

如果你是在一个现有的代码库上工作,很可能已经为你做了决定。如果你正在开始一个项目,你可能想知道你是否应该探索使用RSpec或坚持使用Minitest。如果你想知道你是否应该从一个切换到另一个,这是更细微的差别,而且根据你的代码库的大小,可能不值得这样做。

我希望你能看到你将要写的实际代码,这将有助于你的决策。

以下是我们要比较的主题

设置 - Minitest Vs RSpec

首先,我们将创建两个类似的应用程序并设置RSpec和Minitest。

由于Minitest是与Rails一起安装的,所以它只是使用Rails的new命令:

rails new rails_minitest

然而,在设置RSpec时,我们要做的事情更多:

rails new rails_rspec

在应用程序生成后,我们必须添加RSpec的宝石。这就是我们遇到RSpec的第一个困难的地方。

我们应该添加RSpec核心宝石还是rspec-rails宝石?

选择rspec-rails ,似乎很明显,因为我们要处理的是一个Rails应用,但这是一个错误,在我做顾问的时候,我已经不止一次地看到这个错误。

设置rspec-rails 包括将其添加到我们的Gemfile中的两个位置。

这个项目的readme涵盖了所有这些。

# Gemfile
## For all the generators
group :development, :test do
 gem 'rspec-rails', '~> 5.0.0'
end

在将其添加到你的gem文件后,你可以做以下工作:

bundle install

安装后,我们可以运行rspec:install命令:

rails generate rspec:install

这将创建一个spec 文件夹,一个 .rspec 文件,以及两个额外的文件:spec/spec_helper.rbspec/rails_helper.rb

这两个文件有什么区别?

spec/spec_helper 加载了Rails应用程序,而spec/spec_helper 是RSpec的一个轻量级配置文件。

作为一个简短的旁白,如果你避免为特定的文件加载Rails,你可以加快一些RSpec测试的速度。例如,如果你在应用中使用不需要Rails的普通Ruby对象(PORO),你可以在运行测试时避免将Rails添加到该规范中。这加快了编写代码时的反馈循环,并有助于实现 Tim Ottinger和Jeff Langr所解释的F(ast)in our FIRST原则 。

下面是一个例子。

# app/services/hello_world.rb
class HelloWorld
 def say
  'Hello World'
 end
end
# spec/services/hello_world_spec.rb
# Usually, we require 'rails_helper' here, but there’s no need if we are not using Rails. 
require './app/services/hello_world'

RSpec.describe HelloWorld do

 describe '#hello' do
  it 'returns hello world' do
   expect(HelloWorld.new.say).to eq('Hello World')
  end
 end
end

单元测试:RSpec vs. Minitest

现在我们已经建立了我们的测试套件,我们可以比较我们如何在每个框架中编写测试。

让我们看一下RSpec中的模型测试例子。惯例是先用普通英语写测试,然后用代码写。

require 'rails_helper'

RSpec.describe Article, type: :model do

 context 'validations' do
  article = Article.new
  article.valid?
  it 'must have a title' do
   expect(article.errors.messages[:title]).to include("can't be blank")
  end

  it 'must have a body' do
   expect(article.errors.messages[:body]).to include("can't be blank")
  end
 end
end

运行该测试:

rspec --format documentation spec/models/article_spec.rb

当测试失败时,我们得到以下输出:

Article
 validations
  must have a title (FAILED - 1)
  must have a body (FAILED - 2)

Failures:

 1) Article validations must have a title
   Failure/Error: expect(article.errors.messages[:title]).to include("can't be blank")
    expected ["is too short (minimum is 5 characters)"] to include "can't be blank"
   # ./spec/models/article_spec.rb:9:in `block (3 levels) in <top (required)>'

 2) Article validations must have a body
   Failure/Error: expect(article.errors.messages[:body]).to include("can't be blank")
    expected [] to include "can't be blank"
   # ./spec/models/article_spec.rb:13:in `block (3 levels) in <top (required)>'

Finished in 0.02552 seconds (files took 1.3 seconds to load)
2 examples, 2 failures

Failed examples:

rspec ./spec/models/article_spec.rb:8 # Article validations must have a title
rspec ./spec/models/article_spec.rb:12 # Article validations must have a body

为了得到这个输出,你必须用—format documentation 选项运行RSpec命令。默认的是内联。

接下来,让我们用Minitest编写同样的测试:

require "test_helper"

class ArticleTest < ActiveSupport::TestCase
 setup do
  @article = articles(:one)
 end

 def test_validates_title
  @article.title = nil
  assert @article.valid?
  assert_equal ["can't be blank"], @article.errors[:title]
 end

 def test_validates_body
  @article.body = nil
  assert @article.valid?
  assert_equal ["can't be blank"], @article.errors[:body]
 end
end

现在运行该测试:

rails test test/models/article_test.rb
# Running:

F

Failure:
ArticleTest#test_validates_body [/Users/williamkennedy/projects/honeybadger/test_minitest/test/models/article_test.rb:17]:
Expected: ["can't be blank"]
 Actual: []

rails test test/models/article_test.rb:14

F

Failure:
ArticleTest#test_validates_title [/Users/williamkennedy/projects/honeybadger/test_minitest/test/models/article_test.rb:11]:
Expected: ["can't be blank"]
 Actual: []

rails test test/models/article_test.rb:8



Finished in 0.015373s, 130.0982 runs/s, 260.1964 assertions/s.
2 runs, 4 assertions, 2 failures, 0 errors, 0 skips

直接,你会注意到一件事。Minitest只是Ruby代码,而RSpec是一种需要学习的新语言。虽然语法相似,但RSpec测试的长度会增加。当涉及到RSpec时,还有更多的精神开销,因为你的团队必须为测试的结构定义一个惯例。

对我来说,Minitest只是Ruby,这是一个很大的胜利。Rails通过设置方法在Minitest中建立了一个惯例。

默认的Minitest文件的设置已经考虑到了FIRST原则。

UI测试:RSpec vs. Minitest

现在让我们转到测试的另一个支柱。再一次,Rails默认设置了嵌套在test/system 文件夹下的UI测试。然而,RSpec涉及一些设置,而且没有标准的最佳做法。

一个UI测试是相当复杂的。你编写指令,如click herefill_in ,以驱动交互。他们通常使用网络驱动器,如Selenium,来指导交互。

他们帮助测试JavaScript行为,重新创建可能导致错误的用户旅程,甚至确保特定的用户流是密不可分的。

就其性质而言,它们比常规测试的计算成本更高,这就是为什么我们可能会选择无头CI工具。

自从Rails引入系统测试后,RSpec就受益匪浅。在此之前,我们必须手动设置。

让我们举一个测试的例子。创建一个名为spec/system/article_system_spec.rb 的文件,并添加以下代码。

require 'rails_helper'

RSpec.describe 'Article', type: :system do
 it 'can be created' do
  visit '/articles/new'
  fill_in 'article[title]', with: 'Hello'
  fill_in 'article[body]', with: 'World'
  click_button 'Create'
  expect(page).to have_content 'Article was successfully created.'
 end
end

运行这个测试将产生你所期望的结果。在写这篇文章的时候,它将钩住默认的网络驱动,即Selenium。RSpec允许你通过手动调用driven_by 方法来改变每个测试或甚至所有的规格。

下面是方法:

require 'rails_helper'

RSpec.describe 'Article', type: :system do
 before do
  driven_by(:selenium_chrome_headless)
 end

 it 'should create article' do
  visit '/articles/new'
  fill_in 'article[title]', with: 'Hello'
  fill_in 'article[body]', with: 'World'
  click_button 'Create'
  expect(page).to have_content 'Article was successfully created.'
 end
end

现在测试将使用Selenium_chrome_headless而不是Selenium运行,这可以加快你的测试速度。

由于 Capybara库驱动底层测试,Minitest也有同样的语法。

require "application_system_test_case"

class ArticlesTest < ApplicationSystemTestCase
 setup do
  @article = articles(:one)
 end

 test "should create article" do
  visit articles_url
  click_on "New article"

  fill_in "Body", with: @article.body
  fill_in "Title", with: @article.title
  click_on "Create Article"

  assert_text "Article was successfully created"
  click_on "Back"
 end
end

然而,在每个测试中改变你的网络驱动有一个微妙的区别。

require "application_system_test_case"

class ArticlesTest < ApplicationSystemTestCase
 driven_by :selenium, using: :headless_chrome
 setup do
  @article = articles(:one)
 end

 test "should create article" do
  visit articles_url
  click_on "New article"

  fill_in "Body", with: @article.body
  fill_in "Title", with: @article.title
  click_on "Create Article"

  assert_text "Article was successfully created"
  click_on "Back"
 end
end

你也可以在全局范围内改变这一点:

# test/application_system_test_case.rb
require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
 driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
end

结论

让我们希望这能帮助你在RSpec和Minitest之间做出选择,或者了解其中的区别。我已经尽可能地避免了偏见。这两个框架都有其优点,但都有相同的原则。

先写测试,让它们通过,从长远来看,你会有一个更快乐的代码库。

两者的区别是微妙的。使用RSpec,你会写更多的代码,而且有一个更大的插件生态系统来使事情更容易。然而,当生态系统更大并且由不同的库组成时,当涉及到升级你的应用程序时,依赖树偶尔会造成困难。

不能忽视的一个方面是性能。

Minitest比RSpec快,但魔鬼在细节中。

快多少取决于你如何衡量。这是很难确定的,因为你的测量方式很重要。采样偏差(即用小规模的样本来得出结论)可能有利于Minitest比RSpec快10倍的因素。在其他情况下,差异可能只有10%。

有一个 有趣的库,使用Ruby Benchmark 测量Minitest、RSpec和Cucumber的原始性能,发现Ruby 3的情况如下。

$ bundle exec ruby ./compare.rb
                 user     system      total        real
cucumber:   585.035884  22.566803  607.602687 ( 608.237973)
minitest:    18.208514   7.893526   26.102040 (  26.430622)
rspec:     2406.162561  12.497706 2418.660267 (2418.889164)
test_bench:  29.517226   8.272563   37.789789 (  38.133189)

看这个可能会让你觉得Minitest是压倒性的赢家,因为它运行的时间更短。然而,在实际应用中,情况 可能并非如此。差异可能只有10%,或者根本就没有差异。在比较程序员工具时,由于无数的文化、个人和其他原因,抽样偏见是普遍存在的。

如果你的测试很慢,可能是由于其他一些因素造成的,比如数据库调用、内存,甚至可能是网络调用。

在RSpec和Minitest之间的选择可能只是归结于个人的偏好。

玻璃钢生产厂家常见商场美陈研发成都玻璃钢雕塑推荐厂家白银玻璃钢动物雕塑安装欧式玻璃钢卡通雕塑有哪些曲靖市玻璃钢雕塑厂家电话玻璃钢雕塑制作简易流程玻璃钢雕塑如何仿汉白玉金华抽象玻璃钢雕塑多少钱江苏定制玻璃钢雕塑优势南通玻璃钢仿铜雕塑定制曲阳玻璃钢泡沫雕塑玻璃钢雕塑油漆工艺常年商场门头美陈果洛玻璃钢雕塑定制好看的玻璃钢卡通雕塑玻璃钢雕塑优质商品批发淮南水果玻璃钢雕塑顺义玻璃钢雕塑商场装饰玻璃钢卡通雕塑报价表商场通道灯光美陈工厂商场美陈垃圾分类伊川玻璃钢雕塑定制富阳区玻璃钢雕塑图片汕尾商场雕塑玻璃钢南通玻璃钢景观雕塑设计泰佳成玻璃钢雕塑河南耐高温玻璃钢雕塑摆件玻璃钢雕塑消防商丘玻璃钢卡通雕塑厂家富阳玻璃钢造型雕塑香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化