RSpec初学者测试,第1部分

您是Rails的新手吗? 编码新手? 对RSpec以及如何开始测试感到好奇? 如果是这样,那么本文对于您进行测试驱动的开发应该是一个很好的起点。 它将向您说明原因和方式,并为您提供生存工具包以进行您的首次测试热潮。

主题

  • 重点是什么?
  • RSpec?
  • 入门
  • 运行测试
  • 基本语法
  • 测试的四个阶段
  • 关于测试的难点

重点是什么?

RSpec有什么用处? RSpec在单元测试级别上非常有用,它可以测试应用程序的详细信息和业务逻辑。 这意味着要测试内部模型,例如应用程序的模型和控制器。 涵盖您的视图的测试或模拟更完整的用户流的功能测试(例如购买商品)将不会成为RSpec的重点。 RSpec不使用Web驱动程序,例如Capybara,它可以模拟用户与实际站点或站点的交互。

测试驱动开发(TDD)的意义是什么? 好吧,如果不给您一些陈词滥调,要回答这个问题就不那么容易了。 我希望这不会回避。 我可以提供一个快速的答案,但我想避免只吃少量零食后让您饿着肚子回家。 这个有关RSpec和测试的小系列文章的结果不仅应为您提供所有信息,以您自己回答这个问题,还应为您提供开始测试的方式和理解,同时对测试事物已经充满信心。

与开始使用Ruby或Rails变得危险相比,初学者似乎更难进入RSpec和TDD工作流程。 这是为什么? 我只能在这一点上猜测,但是,一方面,文献似乎主要集中在已经掌握了一定编程技能的人身上,另一方面,学习所有涉及到的东西以使他们有一个清晰的认识是有点令人生畏。 我想学习曲线可能会非常陡峭。 为了进行有效的测试,需要涉及许多活动部件。 要求刚开始了解Rails之类的框架的新手从相反的角度看待构建应用程序的过程,并学习一种全新的API来为您的代码编写代码的新手。

我考虑过如何为正在寻找整件事的下一代编码人员解决这个“难题”。 这就是我想出的。 我将为您分解最基本的语法,而不会假设您仅具备Ruby的基本知识和一点Rails。 我们不会覆盖所有可能的角度并使您困惑而死,我们将介绍您的基本生存工具,并尝试绘制更大的图画。 我们将讨论“如何?” 为了避免在此过程中失去新的编码人员,而进行了冗长的叙述。 等式的第二部分将解释“为什么?”

如果幸运的话,您会为更高级的书籍打下良好的基础,同时对更大的前景充满信心。 好吧,让我们走走吧!

好处之类

让我们回到测试的目的。 测试对于编写质量更高的应用有用吗? 好吧,这可能是一个激烈的辩论,但是目前我想以是的方式回答这个问题-我想我正在赶时髦的TDD阵营。 让我们看看为什么测试为您的应用程序提供了一些难以忽视的好处:

他们检查您的工作是否按预期进行。 不断验证您正在编写有效的代码对于应用程序的运行状况和团队的正常状况至关重要。

他们会测试您不想手动测试的内容,您可以手动完成繁琐的检查,尤其是当您需要一直进行检查时。 您希望尽可能确保您的新功能或新类,或任何不会在应用程序完全不可预见的区域中引起任何副作用的东西。 自动化这类事情不仅可以节省大量时间,还可以使测试方案一致且可重现。 这比手工进行易于出错的测试要可靠得多。

我们要确保应用程序以预期的方式以特定方式运行。 测试可以在很大程度上确保用户与应用程序进行交互的方式正常运行,并避免您可以预见的错误情况。 测试会检查您的应用程序是否按照您设计的方式工作,并且在您引入修改后它可以继续工作。 当您的测试套件通知您有关应用程序实现的失败场景时,这一点尤其重要,因为这些场景可能很旧,因此不再完全是您的大脑,并且在引入某些新功能时也没有考虑。 简而言之,它有助于保持您的应用程序健康,并避免引入大量错误。

自动化测试使您实际上更频繁地进行测试。 想象一下,由于某种原因您是否必须第40次测试某项。 如果只花一点时间,那么无聊并完全跳过该过程有多容易? 这类事情是在湿滑坡道上迈出的第一步,在这里您可以亲吻相当比例的代码覆盖率。

测试作为文档。 ?? 您编写的规范为团队中的其他人员提供了快速入门,以学习新的代码库并了解其功能。 例如,用RSpec编写代码具有很高的表达力,并且形成了可读性强的代码块,这些代码块讲述了一个正确的故事。 因为RSpec可以非常描述性地编写,同时又是一种非常简洁的域特定语言(DSL),所以RSpec可以轻而易举地击中两只鸟:其API并不冗长,并且为您提供了编写高度可理解的测试方案的所有方法。 那就是我一直喜欢的东西,也是为什么我从来没有真正对Cucumber感到满意,我认为Cucumber以一种过于客户友好的方式解决了同样的问题。

它们可以最大程度地减少您编写的代码量。 测试驱动代码的做法使您不必编写疯狂的代码,尝试更多的自由样式,而是只编写通过测试所需的代码。 没有多余的代码。 在未来的职业中,您经常会听到的一件事就是,最好的代码是您不必编写的代码或其他东西。 为什么? 好吧,大多数情况下,更优雅的解决方案涉及的代码量更少,而且您不编写的代码(可能是不必要的)不会导致任何将来的错误,也不需要维护。 因此,在编写实现之前,首先编写测试,可以让您清楚地关注接下来需要解决的问题。 仅编写必需的代码,而不是偶然编写的代码,可能是TDD可以为您提供的被低估的副作用。

它们对您的设计有积极影响。 对我来说,理解这一部分打开了一个灯泡,使我非常欣赏整个测试过程。 当您围绕非常集中的测试场景编写实现时,您的代码很可能会变得更加分隔和模块化。 由于我们都是DRY的朋友(“不要重复自己!”),并且尽可能减少应用程序中组件之间的耦合,因此这是一种简单而有效的方法,可以实现从头开始设计良好的系统。 我认为这是最重要的好处。 是的,其他的也都很棒,但是当测试还由于精致的设计使应用程序的质量更好时,我会说Jackpot!

它也归结为金钱。 如果您拥有一个易于维护且易于更改的稳定应用程序,从长远来看,您将节省很多钱。 不必要的复杂性很容易困扰项目,当您的团队必须与您的代码战斗时,动力就不会达到空前的高度,因为它脆弱且设计不良。 好的应用程序设计可以绝对支持您的业务目标,反之亦然。 您是否要引入一些对您的业务至关重要的新功能,但是由于架构是在沙子上构建的,因此您正在不断与之抗衡? 当然不是,我们所有人都看到了许多因确切原因而Swift消失的商业例子。 在这种情况下,良好的测试习惯可能是有效的防线。

另一个重要的目标是关于代码本身的质量。 您编写的软件对于其他开发人员应该易于理解-至少要尽可能地容易理解。 测试确实可以帮助传达应用程序的功能和意图,不仅可以传达给团队中的其他成员,还可以传达给您自己的未来。 如果您很长一段时间都没有触及代码的特定部分,那么使用RSpec等工具提供的文档来刷新您如何以及为什么编写软件的记忆将非常方便,而RSpec确实可以这真的很好,特别是实际上。

由于您的代码将始终更改,因此重构代码将而且应该始终是开发软件的一部分。 而且,由于更改已融入到此过程中,因此您需要确保这些更改不会在令人惊讶的地方产生意外的副作用。 该测试套件为您提供了一个紧密相连的安全网,使您感到更加舒适,并且可以自由地使用gusto进行重构。 这个方面,除了TDD可以为您提供的设计优势之外,我最喜欢的一个测试套件可以为您提供的优势。 修改和扩展代码是对已经发布的“产品”进行创新的重要组成部分,因此您需要一种工具,该工具可以为您提供尽可能多的自由度。 我不确定对编写广泛的测试套件至关重要的人是否对此方面非常关注。

您将有很大的机会在以后的阶段更快地构建新产品,因为测试套件的反馈将为您提供有关失败,错误和局限性的反馈-当然,这要比人工测试快得多。 另外,它将使您有信心使用安全网络,随着时间的流逝,该安全网络将变得越来越有价值。

在您的应用程序中,尤其是如果它们已经显着增长时,您希望能够信任您的软件。 当您拥有一个已有数百年历史并被数百名开发人员感动的网站时,100%的代码覆盖率听起来更加甜蜜。 能够信任您引入并在此基础上构建的新代码,这是以后无法买到钱的软件开发的一大亮点。

入门

终奌站

rails new your_app -T

-T使您可以跳过Rails附带的测试框架Test Unit。

宝石文件

group :development, :test do
  gem 'rspec-rails'
end

终奌站

bundle

之后,我们需要运行RSpec附带的生成器:

终奌站

rails generate rspec:install

输出量

create  .rspec
create  spec
create  spec/spec_helper.rb
create  spec/rails_helper.rb

这样做是为Rails中的RSpec测试设置基本结构。 从上面的输出中可以看到,此生成器初始化了一个spec目录,其中包含一些稍后需要的文件。 .rspec文件是一个配置文件,我们现在不需要对其进行操作。 我只是想让您知道自己的面前。 其他文件是不言自明的,但是我想提一下它们之间的区别。

  • spec_helper.rb适用于不依赖Rails的规范。
  • 另一方面, rails_helper.rb于确实依赖它的规范。

不明显的是,为了运行测试,需要在spec文件(测试文件)之上需要这些文件之一。 让我们快速浏览一下! 通过以下方式生成模型时:

终奌站

rails generate model dummy_model name:string

输出量

invoke  active_record
create    db/migrate/20160521004127_create_dummy_models.rb
create    app/models/dummy_model.rb
invoke    rspec
create    spec/models/dummy_model_spec.rb

Rails不仅会为您创建关联的_spec.rb文件,而且您的规格默认情况下还将自动在您的规格文件之上require 'rails_helper' 。 这意味着您已经准备好马上出发。

规格/型号/dummy_model_spec.rb
require 'rails_helper'

...

因此,通过此设置,您可以测试Rails应用,例如测试模型,并且RSpec不会对Rails中使用的模型类感到困惑。 每当您需要诸如ActiveRecordApplicationController等之类的东西时,这都是必需的。 因此,这是您的正常情况,因此,作为初学者,这应该是您的第一选择。

另一方面,如果您编写包含Rails应用程序中的业务逻辑的测试,则需要spec_helper.rb会引发错误。 在这种情况下,例如,当您想测试某些Rails模型时,RSpec将不知道您在说什么。

长话短说, spec_helper不会加载Rails -就是这样! 当然,您可以疯狂进行配置,但是我现在不希望您对此感到担心。 让我们专注于基础知识,如何运行测试和语法。 这对于初学者来说足够了。 让我们继续!

运行测试

您已准备好运行测试。 RSpec要求您的测试文件具有特定的后缀,例如_spec以了解要运行的文件。 如果使用生成器,则不必担心,但是如果您想自己编写测试文件,则这是结束它们的方式。 因此,您需要在spec目录中放置一个类似your_first_test_spec.rb的文件。

使用生成器创建虚拟模型已经为我们提供了spec/models/dummy_model_spec.rb 。 不错! 测试准备就绪前要做的一件事:

终奌站

rake db:migrate
rake db:test:prepare

这些命令将为我们上面生成的虚拟模型运行迁移,并使用该模型设置测试数据库。 现在我们实际运行测试:

终奌站

rake

rake命令将运行您的所有测试,即完整的测试套件。 通常,当您完成某些功能并想要练习整个测试套件时,应该使用此命令。

输出量

*
Pending: (Failures listed here are expected and do not affect your suite's status)

1) DummyModel add some examples to (or delete) /Users/vis_kid/projects/rspec-test-app/rspec-dummy/spec/models/dummy_model_spec.rb
   # Not yet implemented
   # ./spec/models/dummy_model_spec.rb:4

Finished in 0.00083 seconds (files took 1.94 seconds to load)

1 example, 0 failures, 1 pending

恭喜你! 您只运行了第一个RSpec测试。 还不错吧? 当然,现在这只是一个虚拟测试-使用Rails生成的虚拟测试代码。 例如,运行测试的更具针对性的版本(实际上,您拥有更多的选择余地)是运行单个文件。 像这样:

终奌站

bundle exec rspec spec/models/dummy_model_spec.rb

这只会运行一个测试文件,而不是整个测试套件。 对于依赖大量测试文件的大型应用程序,这将成为实时保护程序。 但坦率地说,就节省时间和测试特异性而言,这只是表面上的问题。 我认为,在本系列的第三篇文章中进行测试时,我们将介绍如何节省大量时间。 让我们看看我们能走多远!

行使整个测试套件的另一种方法是,只需运行rspec或没有bundle exec,具体取决于您的设置。

终奌站

bundle exec rspec

在继续之前,我还应该提到一件事,您也只能运行特定的测试子集。 假设您只想为模型代码运行所有测试:

终奌站

bundle exec rspec spec/models

那样简单!

基本语法

我建议我们从简单的基础知识开始,然后研究RSpec在接下来的两篇文章中提供的更多选项。 让我们看一下测试的基本结构,并在不熟悉该测试的情况下深入测试。

  • describe

这将成为您的面包和黄油,因为它可以组织您的规格。 您可以自己引用字符串或类:

一些规格

describe User do

end

describe 'Some string' do

end

describe部分是将测试组织为逻辑,连贯的组以进行测试的基本构建块。 基本上,这是您要测试的应用程序不同部分的范围。

一些规格

describe User do
  ...
end

describe Guest do
  ...
end

describe Attacker do
  ...
end

一个不错的技巧是进一步扩大范围。 由于某些类将显着增长,因此在单个describe块中包含要为一个类测试的所有方法并不是一个好主意。 当然,您可以创建多个这些块,并将它们集中在实例或类方法上。 为了使您的意图更清晰,您所要做的就是为类名提供一个额外的字符串,该字符串引用您要测试的方法。

一些规格

describe Agent, '#favorite_gadget' do
  ...
end

describe Agent, '#favorite_gun' do
  ...
end

describe Agent, '.gambler' do
  ...
end

这样一来,您可以两全其美。 您可以将相关测试封装在他们的代表小组中,同时保持重点并保持适当的规模。 对于刚接触Ruby的用户,我应该提到#只是引用了点实例方法. 为类方法保留。 因为它们在字符串中,所以它们在这里没有任何技术含义,但是它们向其他开发人员和您的未来自我传达了您的意图。 不要忘了班级名称后面的逗号-没有它就无法使用! 一分钟后,当我们去expect ,我会告诉你为什么这种做法是超级方便。

  • it

describe组的范围内,我们使用it另一个范围。 这些是针对实际测试示例的。 如果要在Agent类上测试实例方法#favorite_gadget ,它将看起来像这样:

一些规格

describe Agent, '#favorite_gadget' do

  it 'returns one item, the favorite gadget of the agent ' do
    ...
  end

end

您提供给it块的字符串将用作测试的主要文档。 在其中,您可以精确指定要从有关方法中获得或期望的行为类型。 我的建议是不要太过冗长,不要太冗长,但同时又不要太含糊不清,不要使他人与过分聪明的描述混淆。

考虑一下这一部分难题的最小和最简单的实现可以并且应该完成的事情。 您写得越好,应用程序的整体文档就越好。 不要急于使用此部件,因为它只是一根不会造成任何损坏的弦,至少不会在表面上损坏。

  • expect()

现在,我们对事物的核心有了更多的了解。 该方法使您可以验证或伪造要测试的系统部分。 让我们回到前面的示例,并在(受限)操作中看到它:

一些规格

describe Agent, '#favorite_gadget' do

  it 'returns one item, the favorite gadget of the agent ' do
    expect(agent.favorite_gadget).to eq 'Walther PPK' 
  end

end

expect()是RSpec的“新”断言语法。 以前我们使用should代替。 不同的故事,但我想提一提,以防您碰到它。 expect()期望您为它提供一个对象并对其执行任何测试方法。 最后,在右边写出断言的结果。

你必须去与正或负的路线选择.to eq.not_to eq例如( eq做空同等的课程)。 您始终可以改变逻辑,无论哪种方法最适合您的需求。 让我们运行这个无意义的测试,并专注于通过测试设置获得的输出:

终奌站

rspec spec/models/agent_spec.rb

输出量

Failures:

  1) Agent#favorite_gadget returns one item, the favorite gadget of the agent 
     Failure/Error: expect(agent.favorite_gadget).to eq 'Walther PPK'

读起来很不错,不是吗? **"Agent#favorite_gadget返回一项,而agent"**的喜爱小工具agent"**告诉您所有您需要了解的内容:

  • 所涉及的课程
  • 被测方法
  • 预期的结果

如果我们在describe块中省略了描述方法的字符串,那么输出将不那么清晰和可读:

一些规格

describe Agent do

  it 'returns one item, the favorite gadget of the agent ' do
    expect(agent.favorite_gadget).to eq 'Walther PPK' 
  end

end

输出量

Failures:

  1) Agent returns one item, the favorite gadget of the agent 
     Failure/Error: expect(agent.favorite_gadget).to eq 'Walther PPK'

当然,还有其他方法可以规避和处理此问题(例如,通过您的it块传递此信息),但是另一种方法很简单且可行。 当然,任何使您的血液流动!

测试的四个阶段

测试方面的最佳做法建议我们在四个不同的阶段进行测试:

  • 测试设置
  • 测试练习
  • 测试验证
  • 测试拆解

这四个阶段主要是为了提高可读性并使测试具有常规结构。 基本上,这是一种所谓的测试模式,是社区广泛认可的一种有用和推荐的做法。 整个模式主题都是一个深深的兔子洞,所以请知道我会省略一堆,以免使您中的初学者迷惑至死。

建立

在安装过程中,您准备了应该在其中运行测试的方案。 在大多数情况下,这将包括准备进行某种练习所需的数据。 小提示:不要使事情复杂化,并且只设置使测试工作所需的最小数量。

agent = Agent.create(name: 'James Bond')
mission = Mission.create(name: 'Moonraker', status: 'Briefed')

行使

这部分实际上运行了您要在此规范中测试的东西。 可能很简单:

status = mission.agent_status

校验

现在,您可以验证关于测试的声明是否得到满足。 因此,您可以根据自己的期望来测试系统。

expect(status).not_to eq 'MIA')

拆除

该框架处理内存和数据库清理问题-基本上是重置。 在这一点上,您没有什么要处理的。 目标是使原始状态恢复到运行新测试的状态,而不会引起当前正在运行的测试的任何意外。 让我们看看这是一个虚拟示例的含义:

一些规格

describe Agent, '#favorite_gadget' do

  it 'returns one item, the favorite gadget of the agent ' do
  # Setup
    agent = Agent.create(name: 'James Bond')
    q = Quartermaster.create(name: 'Q') 
    q.technical_briefing(agent)

  # Exercise    
    favorite_gadget = agent.favorite_gadget
  
  # Verify
    expect(favorite_gadget).to eq 'Walther PPK' 

  # Teardown is for now mostly handled by RSpec itself
  end

end

如您所见,在此示例中,我们将练习与阶段彼此清晰地分开,而在上述其他虚拟示例中, expect(agent.favorite_gadget).to eq 'Walther PKK ,我们将两个阶段混合在一起。 两者都是有效的方案,并且都有自己的位置。 此外,新行还有助于从视觉上区分测试的结构。

关于测试的难点

现在是最困难的部分,要测试什么以及如何测试。 我认为,这是测试方面最令新手感到困惑的方面,这是可以理解的! 您是语言和框架的新手,通常甚至不知道您不知道什么。 您如何为此编写测试? 很好的问题。

我会很坦率的说,您很可能不会,而且很长一段时间都不会。 熟悉这些东西需要一段时间。 当您有导师或参加一些新手训练营时,您就有机会直接向经验丰富的人学习。 当然,在这种情况下,您在该部门的进度会有所不同。

另一方面,如果您像其他许多人一样在自学这些东西,那么耐心将是关键。 阅读所有书籍和文章当然可以使您朝正确的方向前进,但是我认为测试需要准备许多更高级的拼图,以便让您完全理解,甚至可能更重要,然后再对它感到满意。 。

“好”消息是这并不罕见,我们所有人都去过那里。 毅力很重要。 您可以做到这一点,这不是火箭科学,但是要花一点时间才能从另一种角度有效地编写应用程序-从测试的角度来看。 现在,继续努力,找乐子,犯错误,编写应用程序,复制教程等等,直到灯泡熄灭为止。

最后的想法

编写单个测试时,您希望使它们的对象尽可能简单地实现目标-高度专注的测试才是真正的关键。 您想通过非常简单的步骤来设计应用程序,然后遵循测试套件所提供的错误。

仅实施使应用程序绿色环保所需的内容。 不多不少! 这是测试驱动开发中的“驱动”部分。 您的工作取决于测试需求。

翻译自: https://code.tutsplus.com/articles/rspec-testing-for-beginners-part-1--cms-26716

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值