测试Ruby on Rails应用程序的方法有很多,但有一种方法并不经常被讨论。是的--有控制器、模型和其他类型的测试,但我们很少看到视图层的测试。让我们给他们更多的关注,看看他们到底是怎么一回事。
你可能会问--好吧,你为什么不写集成测试,用它们来测试视图层?我可以,但运行集成测试可能很慢,而写一个简单的视图 "单元 "测试可能更直接。另外,RSpec对编写视图规范有很大的支持。更有趣的是,我创建了一个例子项目来测试这些。让我们看看我发现了什么。
新项目, 谁这
我创建了新的Rails 6.1项目,安装了RSpec,并生成了Book模型:
bin/rails generate scaffold Book title:string description:text download_url:string status:string
看看我所生成的东西:
...
create spec/views/books/edit.html.erb_spec.rb
create spec/views/books/index.html.erb_spec.rb
create spec/views/books/new.html.erb_spec.rb
create spec/views/books/show.html.erb_spec.rb
...
如果我们看一下其中一个规格,我们可以发现以下代码:
# spec/views/books/index.html.erb_spec.rb
require 'rails_helper'
RSpec.describe "books/index", type: :view do
before(:each) do
assign(:books, [
Book.create!(
title: "Title",
description: "MyText",
download_url: "Download Url",
status: "Status"
),
Book.create!(
title: "Title",
description: "MyText",
download_url: "Download Url",
status: "Status"
)
])
end
it "renders a list of books" do
render
assert_select "tr>td", text: "Title".to_s, count: 2
assert_select "tr>td", text: "MyText".to_s, count: 2
assert_select "tr>td", text: "Download Url".to_s, count: 2
assert_select "tr>td", text: "Status".to_s, count: 2
end
end
这里有type: view ,表示规格的特殊类型。我们稍后会深入讨论这个问题。你可以分辨出assign 和render 方法,表示它们是内部定义的,不是我们应该提供的。但是,当我通过这个测试时,还有一件事刺入我的眼睛。
有趣的是,这个assert_select 匹配器看起来有点 "废弃",或者说它不是来自RSpec世界。没有类expect(...).to形成。这里发生的事情是,生成这些规范的模板有点蒙尘了。从2010年开始,它并没有改变,当时它最初被推送到repo。你可以在GitHub上找到带来assert_select的提交。
不用担心,我没有看到很多项目使用视图规格,更不用说生成模型并依赖这些生成的视图规格了。我想这就是为什么没有人抽出时间来重构或改进现有的模板。但是,既然这篇博文是关于将我们的注意力集中在Rails视图测试上,让我们试着去做。
春季大扫除
如果我们看一下RSpec中视图规格的文档,我们可以看到,几乎所有的视图规格都使用了以下内容:
expect(rendered).to match /something/
我们可以从RSpec中使用match 和include 。我们得到的是一个看起来像这样的测试:
# spec/views/books/index.html.erb_spec.rb
require 'rails_helper'
RSpec.describe "books/index", type: :view do
before(:each) do
assign(:books, [
Book.create!(
title: "Rails Testing",
description: "How to test Ruby on Rails applications.",
download_url: nil,
status: "draft"
),
Book.create!(
title: "Rails Patterns",
description: "A book about patterns and anti-patterns in Ruby on Rails.",
download_url: "rails-patterns.com/download",
status: "published"
)
])
end
it "renders a list of books" do
render
expect(rendered).to match(/Rails Testing/)
expect(rendered).to include("Rails Patterns")
expect(rendered).to match(/How to test Ruby on Rails applications./)
expect(rendered).to include("A book about patterns and anti-patterns in Ruby on Rails.")
expect(rendered).to include("rails-patterns.com/download")
expect(rendered).to include("published")
end
end
前面的测试感觉更像是一个RSpec规范。但是,我们可以注意到的是,我们失去了那种检查实际内容是否在某个HTML标签内的能力。assert_select 在匹配预期结果方面给了我们更多的灵活性。在它的文档中,你可以传递给assert_select 更多的选项。我建议你选择你觉得能给你更多控制的选项。
利用Capybara
如果你安装了Capybara,你可以像这样利用它的选择器:
require "rails_helper"
RSpec.describe "books/index", type: :view do
before(:each) do
assign(:books, [
Book.create!(
title: "Rails Testing",
description: "How to test Ruby on Rails applications.",
download_url: nil,
status: "draft"
),
Book.create!(
title: "Rails Patterns",
description: "A book about patterns and anti-patterns in Ruby on Rails.",
download_url: "rails-patterns.com/download",
status: "published"
)
])
end
it "renders a list of books" do
render
expect(rendered).to have_selector("tr>td", text: "Rails Testing")
expect(rendered).to have_selector("tr>td", text: "Rails Patterns")
expect(rendered).to have_selector("tr>td", text: "How to test Ruby on Rails applications")
expect(rendered).to have_selector("tr>td", text: "A book about patterns and anti-patterns in Ruby on Rails.")
expect(rendered).to have_selector("tr>td", text: "rails-patterns.com/download")
expect(rendered).to have_selector("tr>td", text: "published")
end
end
现在,你既得到了RSpecexpect(...).to ,又得到了断言文本在表格行内的粒度。你可以在这里找到所有的代码和例子。但你为什么要使用这些呢?让我们在下面讨论一下。
为什么查看规格
我们略过了你为什么要写视图规范的几个原因。这个想法是为了测试你在视图或部分中的一些条件逻辑。编写一个涵盖视图中所有分支的集成测试,运行起来很慢,编写起来也很痛苦。视图规范在以下方面带来了很大的平衡:
- 💸开发成本。
- 🏍执行速度,以及
- 🔀条件渲染覆盖。
当然,如果你有装饰器、视图模型、表单对象和所有其他可以将逻辑移出视图的好东西,你可能根本就不需要视图规格。但是,有时候,在现实世界中,并不是每一个代码库都是完美的设计,你必须不时地走弯路。
无论是某种利益相关者在你的脖子上呼吸。或者是复杂的遗留部分,不能那么容易提取到你选择的设计。不管是什么原因,你可能会选择加入视图规范,以便快速行动并对逻辑进行测试。
而当这一天到来时(或者已经到来),你可以回到这篇博文,并根据你的喜好使用它。
如果你喜欢这篇文章,你可以在Twitter上分享它。考虑订阅通讯,以获得像这样的新文章。
下一篇再会,干杯。