测试后端【免费下载链接】rspec-railsRSpec for Rails 7项目地址https://gitcode.com/gh_mirrors/rs/rspec-rails点击查看免费下载本篇指南围绕当前仓库rspec-rails的 README.md 展开系统讲解如何将 RSpec 作为 Rails 默认测试框架 Minitest 的替代方案接入应用包括版本选择策略、Gemfile 配置与rspec:install引导、基于rails generate的规格文件生成、rspec命令行的运行方式、RSpec DSL 的写法以及 Rails 专属匹配器matchers与十种 spec 类型的适用场景。读者在阅读后将具备在 Rails 8.x/7.x 项目中完整落地 rspec-rails 并编写、运行、调试规格的实战能力。什么是 rspec-rails测试脚本与规格Spec的区别rspec-rails将 RSpec 测试框架带到 Ruby on Rails 应用中作为 Rails 默认测试框架 Minitest 的即插即用drop-in替代品。它最核心的理念是在 RSpec 中测试不仅仅是验证应用代码的脚本它们同时也是规格specs——用近乎平白的英文描述应用应该如何表现的详细说明。也就是说同一个文件里既包含应用应该有什么行为的描述性文字又包含验证这些行为的测试代码。RSpec 运行后会生成一份可读性极强的报告告诉你在哪些地方应用符合规格、哪些地方不符合。这种先描述、后验证的方式让测试套件同时充当了可执行文档的角色。从仓库源码可以看到rspec-rails通过一个 Railtie 在应用启动时自动完成挂载lib/rspec-rails.rb向rails notes注册spec目录向 Rails 8.0 的rails stats注册各 spec 目录的统计信息设置integration_tool :rspec与test_framework :rspec使 Rails 内置生成器生成的代码自动委托给 RSpec加载specRake 任务lib/rspec/rails/tasks/rspec.rake并默认把default任务指向spec自动配置 ActionMailer 预览路径将其指向#{Rails.root}/spec/mailers/previews。这些机制保证了安装后无需额外配置rake spec、rails generate就能立即协同工作。支持的版本与版本策略RSpec Rails 遵循 RSpec Rails 版本策略 RFC 中定义的版本策略只支持当前处于支持期的 Rails 版本并在移除旧版本支持时发布 major 版本升级。核心规则如下majorx包含破坏性变更以及对 Rails 支持范围的调整移除不再支持的 Rails 版本视为破坏性变更minory新增功能、新增对新的 Ruby/Rails 版本的适配patchz仅包含 bug 修复。据此选择你应使用的版本rspec-rails 版本适用的 Rails 版本8.xRails 8.0 或 7.27.xRails 7.x6.xRails 6.1、7.0 或 7.15.xRails 5.2 或 6.x4.xRails 5.x 或 6.x3.xRails 5.0 之前1.xRails 2.x仓库当前开发分支对应的版本为8.1.0.pre见 lib/rspec/rails/version.rb属于 8.x 主线。升级注意事项可参考 features/upgrade/README.md例如 7.x 支持 Rails 7.0、7.1、7.2从 6.x 升级到 7 无需修改代码而 4.x 是首个脱离 RSpec 主线同步版本号的 major 版本其发布即代表着对 Rails 支持范围的收紧。安装注意仓库的 README/main 分支指向当前开发构建8.1.0.pre如果需要或要求最新的稳定版请使用8-0-maintenance分支对应发布的版本。1. 将 gem 加入 Gemfile需要同时加入应用的:development和:test两个组# 使用稳定版 group :development, :test do gem rspec-rails, ~ 8.0.0 end # 或使用 main 分支 group :development, :test do gem rspec-rails, git: https://github.com/rspec/rspec-rails end将 gem 加入:development组并非严格必需但如果没有它生成器和 rake 任务都必须以RAILS_ENVtest为前缀才能正常工作。2. 安装并引导# 下载并安装 $ bundle install # 生成样板配置文件 # 每个生成文件中的注释包含更多说明 $ rails generate rspec:install create .rspec create spec create spec/spec_helper.rb create spec/rails_helper.rb从 lib/generators/rspec/install/install_generator.rb 的源码可以看到rspec:install生成器会在临时目录中调用RSpec::Core::ProjectInitializer生成.rspec与spec/spec_helper.rb并把其中的rspec --init注释替换为rails generate rspec:install、移除config.warnings true配置支持--default-path选项默认spec若指定了非spec的默认路径会自动向.rspec追加--default-path path复制spec/rails_helper.rb模板lib/generators/rspec/install/templates/spec/rails_helper.rb。spec/rails_helper.rb是 Rails 相关的 RSpec 配置核心其关键配置项包括require spec_helper ENV[RAILS_ENV] || test require_relative ../config/environment abort(The Rails environment is running in production mode!) if Rails.env.production? require rspec/rails # Add additional requires below this line. Rails is not loaded until this point! RSpec.configure do |config| # 使用 ActiveRecord 时 config.fixture_paths [Rails.root.join(spec/fixtures)] config.use_transactional_fixtures true # 不想使用 ActiveRecord 时取消注释 # config.use_active_record false # 取消注释以按文件位置推断 spec 类型该行为被视为遗留特性未来版本将移除 # config.infer_spec_type_from_file_location! # 从回溯中过滤 Rails 相关 gem 的行 config.filter_rails_from_backtrace! # 任意 gem 也可过滤 # config.filter_gems_from_backtrace(gem name) end模板还会在 ActiveRecord 场景下调用ActiveRecord::Migration.maintain_test_schema!确保测试数据库 schema 与当前 schema 文件一致存在未执行迁移时会以db:test:prepare重建测试数据库否则中止并给出提示。升级如果项目已在用旧版 rspec-rails升级到最新版$ bundle update rspec-railsRSpec 遵循语义化版本这意味着 major 版本升级如 2.x → 3.x会带来破坏性变更。从 2.x 或更低版本升级时务必阅读 rspec-rails 升级说明与通用的 RSpec 升级说明了解需要注意的事项。结合 features/upgrade/README.md 的说明升级时还需注意RSpec 团队只维护当前 major/minor 版本main分支常包含对下一版本 Rails 的适配直接使用该分支也是受支持的。例如从 6.x 升到 7 时建议先升级到 Rails 7.1 rspec-rails 6.1.x再升级到 Rails 7.2 rspec-rails 7.0停留在 Rails 6.1 的项目应继续使用 rspec-rails 6.1。使用用rails generate生成规格文件# RSpec 挂接 Rails 内置生成器 $ rails generate model user invoke active_record create db/migrate/20181017040312_create_users.rb create app/models/user.rb invoke rspec create spec/models/user_spec.rb # RSpec 也提供自己的规格文件生成器 $ rails generate rspec:model user create spec/models/user_spec.rb # 列出所有 RSpec 生成器 $ rails generate --help | grep rspec这里体现了 lib/rspec-rails.rb 中generators.integration_tool :rspec与generators.test_framework :rspec的作用执行rails generate model user时Rails 生成完模型与迁移后会自动调用 RSpec 生成对应的spec/models/user_spec.rb。仓库内置了覆盖各类型的一整套生成器目录 lib/generators/rspec/rspec:install——引导安装rspec:modelmodel_generator.rb——生成模型规格与 fixtures.ymlrspec:controllercontroller_generator.rb——提供--request-specs默认 true、--controller-specs默认 false、--view-specs默认 true、--routing-specs默认 false等选项rspec:requestrequest_generator.rb——生成请求规格rspec:systemsystem_generator.rb——生成系统规格以及rspec:feature、rspec:helper、rspec:job、rspec:mailer、rspec:mailbox、rspec:channel、rspec:scaffold、rspec:generator、rspec:authentication等。运行规格# 默认运行所有规格文件即匹配 spec/**/*_spec.rb 的文件 $ bundle exec rspec # 递归运行单个目录下的所有规格文件 $ bundle exec rspec spec/models # 运行单个规格文件 $ bundle exec rspec spec/controllers/accounts_controller_spec.rb # 按行号运行单个 example $ bundle exec rspec spec/controllers/accounts_controller_spec.rb:8 # 查看运行规格的全部选项 $ bundle exec rspec --help可选如果觉得bundle exec rspec太长可以生成bin/rspecbinstub 直接使用$ bundle binstubs rspec-core此外仓库的 Rakefile 与 lib/rspec/rails/tasks/rspec.rake 还提供rake spec任务以及按 spec 子目录自动生成的rake spec:models、rake spec:requests等分类型任务运行前会执行spec:prepare将RAILS_ENV置为test并触发 Rails 的test:prepare。RSpec DSL 基础怎么写一个 spec在 RSpec 中应用行为先以近乎平白的英文描述再用测试代码描述RSpec.describe Post do # context before publication do # (几乎)平白的英文 it cannot have comments do # expect { Post.create.comments.create! }.to raise_error(ActiveRecord::RecordInvalid) # 测试代码 end end end运行rspec会执行这段测试代码并用这些平白的英文描述生成一份报告展示应用在哪些地方符合或不符合规格$ rspec --format documentation spec/models/post_spec.rb Post before publication cannot have comments Failures: 1) Post before publication cannot have comments Failure/Error: expect { Post.create.comments.create! }.to raise_error(ActiveRecord::RecordInvalid) expected ActiveRecord::RecordInvalid but nothing was raised # ./spec/models/post.rb:4:in block (3 levels) in top (required) Finished in 0.00527 seconds (files took 0.29657 seconds to load) 1 example, 1 failure Failed examples: rspec ./spec/models/post_spec.rb:3 # Post before publication cannot have comments这段示例同时展示了 RSpec 的常用元素describe用于描述被测对象Postcontext用于划分行为场景发布前it定义单个断言例子expect {...}.to raise_error(...)用于断言抛出异常。报告末尾还会给出rspec 文件:行号的重跑命令方便定位失败的 example。Rails 专属匹配器Matchers在 RSpec 中断言被称为期望expectations每条期望都基于一个匹配器matcher。当写expect(a).to eq(b)时使用的就是eq匹配器。除了 RSpec 内置的标准匹配器rspec-rails 还提供了一组让测试 Rails 系统各部分更轻松的额外匹配器RSpec 匹配器委托给可用场景备注be_a_new—所有主要面向 controller specsrender_templateassert_templaterequest / controller / view与expect(response).to一起使用redirect_toassert_redirectrequest / controller与expect(response).to一起使用route_toassert_recognizesrouting / controller与expect(...).to route_to一起使用be_routable—routing / controller与expect(...).not_to be_routable一起使用have_http_status—request / controller / feature—match_array—所有用于比较 ActiveRecord 对象数组have_been_enqueued—所有需要配置ActiveJob::Base.queue_adapter :testhave_enqueued_job—所有需要配置ActiveJob::Base.queue_adapter :test仓库中的对应实现都位于 lib/rspec/rails/matchers/由 matchers.rb 统一加载并且会根据当前环境特性ActiveJob、Action Cable、Action Mailbox 是否可用条件加载相关匹配器。have_http_statusHTTP 状态断言该匹配器接受三种形式的参数have_http_status.rb数字状态码expect(response).to have_http_status(209)Rack::Utils::SYMBOL_TO_STATUS_CODE中定义的符号状态名expect(response).to have_http_status(:see_other)通用状态类型:success、:missing、:redirect、:errorexpect(response).to have_http_status(:error)源码中matcher_for_status会根据参数形式分别构造GenericStatus、SymbolicStatus或NumericCode匹配器。它在 controller、request、feature 三种 spec 中都可使用例如 request spec 中RSpec.describe Widget management, type: :request do it creates a Widget and redirects to the Widgets page do get /widgets/new expect(response).to have_http_status(:ok) post /widgets, params: { widget: { name: My Widget } } expect(response).to have_http_status(302) follow_redirect! expect(response).to have_http_status(:success) end end详见 features/matchers/have_http_status_matcher.feature 的完整场景。be_a_new断言新建未保存的记录be_a_new主要用于 controller specs断言实例变量是一个指定模型类的、尚未保存的新记录并可通过with指定要匹配的属性be_a_new.rbget :new assigns(:thing).should be_a_new(Thing) post :create, thing: { name: Illegal Value } assigns(:thing).should be_a_new(Thing).with(name: nil)match_array比较 ActiveRecord 关系与数组relation_match_array.rb 为ActiveRecord::Relation注册了~运算符使其行为等价于 RSpec 内置的ContainExactly匹配器从而可以方便地将查询结果与期望数组做无序比较详见 features/matchers/relation_match_array.feature。ActiveJob 相关匹配器have_enqueued_job别名enqueue_job与have_been_enqueued用于断言 job 是否被入队需要先设置ActiveJob::Base.queue_adapter :test。支持的断言形式包括按 job 类、按入队参数.with也可用块做参数断言、按入队时间.at(Date.tomorrow.noon)/.at(:no_wait)、按队列名.on_queue(default)RSpec.describe UploadBackupsJob do it matches with enqueued job do ActiveJob::Base.queue_adapter :test expect { UploadBackupsJob.set(wait_until: Date.tomorrow.noon).perform_later }.to have_enqueued_job(UploadBackupsJob).at(Date.tomorrow.noon) end end完整场景见 features/matchers/have_enqueued_job_matcher.feature。此外还有have_enqueued_mailhave_enqueued_mail.rb继承自HaveEnqueuedJob与send_emailsend_email.rb等邮件相关匹配器。该写哪些测试十种 spec 类型RSpec Rails 为测试典型 Rails 应用的不同部分定义了十种 spec 类型。每一种都继承自 Rails 内置的某个TestCase类这意味着 Rails 测试默认提供的辅助方法在 RSpec 中同样可用Spec 类型对应的 Rails 测试类model—controllerActionController::TestCasemailerActionMailer::TestCasejob—viewActionView::TestCaserouting—helperActionView::TestCaserequestActionDispatch::IntegrationTestfeature—systemActionDispatch::SystemTestCase创建 spec 文件时在顶层的describe块中指定类型# spec/models/user_spec.rb RSpec.describe User, type: :model do ...从 lib/rspec/rails/configuration.rb 的add_test_type_configurations可以看到rspec-rails 会按类型把对应的 ExampleGroup 模块注入 RSpec 配置config.include RSpec::Rails::ControllerExampleGroup, type: :controller config.include RSpec::Rails::HelperExampleGroup, type: :helper config.include RSpec::Rails::ModelExampleGroup, type: :model config.include RSpec::Rails::RequestExampleGroup, type: :request config.include RSpec::Rails::RoutingExampleGroup, type: :routing config.include RSpec::Rails::ViewExampleGroup, type: :view config.include RSpec::Rails::FeatureExampleGroup, type: :feature config.include RSpec::Rails::SystemExampleGroup, type: :system各 ExampleGroup 的实现位于 lib/rspec/rails/example/所有类型共享 rails_example_group.rb 中的RailsExampleGroup它组合了 Setup/Teardown 适配器、Minitest 生命周期与断言适配器、Fixture 支持、TaggedLogging 适配器以及ActiveSupport::CurrentAttributes与ExecutionContext的测试辅助request_example_group.rb 组合ActionDispatch::Integration::Runner、断言模块与RedirectTo/RenderTemplate匹配器并把app委托给Rails.applicationcontroller_example_group.rb 组合ActionController::TestCase::Behavior与视图渲染支持并提供controller do ... endDSL 用于测试ApplicationControllermodel_example_group.rb 当前未额外提供特殊能力仅继承公共的RailsExampleGrouphelper_example_group.rb 与 view_example_group.rb 组合ActionView::TestCase::Behavior后者还提供render、view、stub_template、params等 DSLmailer_example_group.rb 与 job_example_group.rb 仅在 ActionMailer / ActiveJob 已加载时才生效。注意这不是一份检查清单。问一百个开发者怎么测试应用会得到一百个不同的答案。 RSpec Rails 提供了经过深思熟虑挑选的功能以鼓励良好的测试实践但没有所谓正确的做法。 最终如何组织你的测试套件取决于你自己。如果想按文件位置自动推断类型可取消注释rails_helper.rb中的config.infer_spec_type_from_file_location!。它依据 configuration.rb 中的DIRECTORY_MAPPINGS映射如spec/models→:model、spec/requests/spec/integration/spec/api→:request、spec/features→:feature等自动为相应目录下的 specs 打上类型元数据README 与模板均注明该行为被视为遗留特性未来版本将移除因此建议显式声明type:。System specs、Feature specs、Request specs 有什么区别RSpec Rails 提供了一些端到端整个应用的测试能力用于规格化与客户端的交互。System specs也称为验收测试acceptance tests、浏览器测试browser tests或端到端测试end-to-end tests。系统规格站在人类客户端的视角测试应用测试代码模拟用户在浏览器中的操作visit /login fill_in Name, with: jdoe期望则围绕页面内容展开expect(page).to have_text(Welcome)因为系统规格是 Rails 内置SystemTestCase的封装所以仅在 Rails 5.1 可用feature specs 有相同用途但不依赖它。从 system_example_group.rb 可以看到其内部默认驱动为:selenium_chrome_headless可通过driven_by覆盖硬性依赖 Capybara 与 Web 服务器如Capybara.server :puma并接管了 Rails 的 before/after teardown 钩子以保证失败截图等能力正常工作。Feature specs在 Rails 引入系统测试能力之前feature specs 是唯一的端到端测试类型。虽然 RSpec 团队现在官方推荐使用 system specs但 feature specs 依然得到完全支持、写法基本相同并能在较老的 Rails 版本上运行。另一方面feature specs 需要相当多的配置才能让一些重要特性正常工作比如 JavaScript 测试、保证每个测试都在全新的数据库状态下运行而 system specs 开箱即用地提供了这些配置。与 system specs 一样feature specs 需要 Capybara gem。Rails 5.1 已将其作为系统测试的一部分默认包含但如果你没有升级的条件请先在 Gemfile 的:test组中加入group :test do gem capybara end从 feature_example_group.rb 源码可以看到feature specs 提供RSpec.feature/scenario别名默认 host 为www.example.com并会在 Capybara 未加载时给出明确的报错提示。Request specs请求规格从机器客户端的视角测试应用始于一个 HTTP 请求终于该请求的 HTTP 响应。它们比 feature specs 更快但不会检查应用的 UI 或 JavaScript。请求规格是 controller specs 的高层替代方案。事实上从 RSpec 3.5 起Rails 与 RSpec 两个团队都建议不要直接测试控制器而应优先使用 request specs 这类功能测试。写请求规格时尝试回答这个问题对于给定的 HTTP 请求动词 路径 参数应用应该返回什么样的 HTTP 响应事务与测试数据隔离rails generate rspec:install生成的spec/rails_helper.rb中包含RSpec.configure do |config| config.use_transactional_fixtures true end这个配置项的名字有一定误导性。它在 Rails 中的真实含义是在每个测试方法内运行事务在 rspec-rails 的语境下就是在每个 example 内运行事务。其思路是每个 example 从干净的数据库开始创建该 example 所需的数据然后通过在 example 结束时回滚事务来移除这些数据详见 features/Transactions.md。关闭事务如果你希望自行管理数据或使用其他工具如 database_cleaner只需让 RSpec 告诉 Rails 不要管理事务RSpec.configure do |config| config.use_transactional_fixtures false endbefore(:example)中创建的数据会被回滚。每个 example 之间相互隔离例如describe Widget do before(:example) do widget Widget.create end it does something do expect(widget).to do_something end it does something else do expect(widget).to do_something_else end end上面的两个 example 中widget都会被重新创建因此每个 example 拿到的是不同对象且底层数据在回滚后各自全新。before(:context)中创建的数据不会回滚。:context钩子在事务开启之前执行可用于在组内任何 example 运行前一次性创建数据以加速测试但会引入一系列复杂问题只有充分理解其影响时才应使用。两条准则务必在after(:context)钩子中清理数据before(:context) do widget Widget.create! end after(:context) do widget.destroy end否则残留数据迟早会干扰其他 examples。在before(:example)中重新加载对象before(:context) do widget Widget.create! end before(:example) do widget.reload end因为每个 example 中的数据库更新会被回滚但对象本身并不知道这些回滚对象与其底层数据很容易不同步。从 lib/rspec/rails/fixture_support.rb 源码可以看到FixtureSupport在 ActiveRecord 场景下混入ActiveRecord::TestFixtures并依据 RSpec 配置的use_active_record?、fixture_paths、use_transactional_fixtures、use_instantiated_fixtures与global_fixtures进行初始化同时会在before :context作用域调用 fixture 方法时发出警告。这些配置项的定义可查阅 lib/rspec/rails/configuration.rb 与 lib/rspec/rails/active_record.rb后者还会在声明 verifying double 时自动为 ActiveRecord 模型定义属性方法以支持动态列。动手实践从零跑通一个 RSpec Rails 应用参考仓库 features/Getting_started.md 的完整入门流程# 1. 安装 Rails 并生成应用 $ gem install rails -v ~ 7.2.0 $ rails new example_app $ cd example_app # 2. 把 rspec-rails 加入 Gemfile $ echo gem rspec-rails, group: [:development, :test] Gemfile # 3. 安装依赖 $ bundle install # 4. 引导 RSpec $ rails generate rspec:install # 5. 生成 scaffoldapp 目录由 Rails 生成spec 目录委托给 RSpec $ rails generate scaffold Widget name:string # 6. 执行迁移 $ rails db:migrate rails db:test:prepare # 7. 运行规格 $ rake spec # 或 $ rspec spec --format documentation如果一切顺利输出应以类似29 examples, 0 failures, 2 pending结尾并伴随一份按控制器 / action / 行为层级展开的文档化报告WidgetsController GET index assigns all widgets as widgets……可以快速建立对被测对象行为的高层理解也能一眼看出哪些用例尚未覆盖——例如create与update两个 action 若缺了redirects to the widget之类的断言会很容易被发现。结语rspec-rails的定位是 Rails 生态中的规格驱动测试接入层通过 Railtie 挂接生成器与 rake 任务通过类型化 ExampleGroup 继承 Rails 的各类TestCase能力再以一批 Rails 专属匹配器补齐 HTTP 状态、渲染、路由、ActiveJob 等断言语义。它的版本号直接与 Rails 支持周期对齐使用时应优先按当前 Rails 版本选择对应的 rspec-rails major 版本。更多细节可继续阅读仓库中的 features/ 目录Cucumber 场景文档、lib/rspec/rails/ 源码与 spec/ 测试深入了解每个匹配器与 ExampleGroup 的具体行为。赞分享测试后端【免费下载链接】rspec-railsRSpec for Rails 7项目地址https://gitcode.com/gh_mirrors/rs/rspec-rails点击查看免费下载相关推荐rspec-rails 起步指南在 Rails 7 应用中用 5 分钟完成 RSpec 测试环境搭建rspec rails 起步指南在 Rails 7 应用中用 5 分钟完成 RSpec 测试环境搭建 导读 本文是基于 rspec rails 官方 Get测试后端rspec-rails测试驱动开发实践如何用测试驱动Rails应用开发rspec rails测试驱动开发实践如何用测试驱动Rails应用开发 测试驱动开发TDD是现代软件开发的核心实践之一而rspec rails作为RSp测试后端Ruby on Rails 单元测试实战使用 RSpec 为模型逻辑编写单元测试Ruby on Rails 单元测试实战使用 RSpec 为模型逻辑编写单元测试 本文围绕 unit_testing.md https://link.gitc文档教程教育上一篇Voca社区贡献指南如何参与这个开源JavaScript字符串库的开发与维护下一篇Lucky 反向代理一个域名接入多台服务的完整配置流程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考