- Testing best practices
- Testing best practices
- Test Design
- Test speed
- RSpec
- General guidelines
- Coverage
- System / Feature tests
- Fast unit tests
let
variables- Common test setup
- Time-sensitive tests
- Feature flags in tests
- Pristine test environments
- Table-based / Parameterized tests
- Prometheus tests
- Matchers
- Testing query performance
- Shared contexts
- Shared examples
- Helpers
- Factories
- Fixtures
- Repositories
- Configuration
- Test environment logging
Testing best practices
原文:https://docs.gitlab.com/ee/development/testing_guide/best_practices.html
- Test Design
- Test speed
- RSpec
- General guidelines
- Coverage
- System / Feature tests
- Fast unit tests
let
variables- Common test setup
- Time-sensitive tests
- Feature flags in tests
- Pristine test environments
- Table-based / Parameterized tests
- Prometheus tests
- Matchers
- Testing query performance
- Shared contexts
- Shared examples
- Helpers
- Factories
- Fixtures
- Repositories
- Configuration
- Test environment logging
Testing best practices
Test Design
在 manbetx 客户端打不开的测试是头等公民,而不是事后的想法. 在设计功能时,必须考虑测试的设计,这一点很重要.
在实现功能时,我们考虑以正确的方式开发正确的功能,这有助于我们将范围缩小到可管理的水平. 在对功能进行测试时,我们必须考虑开发正确的测试,然后涵盖测试可能失败的所有重要方式,这可能很快将我们的范围扩大到难以管理的水平.
测试启发法可以帮助解决此问题. 它们简明地解决了错误在我们的代码中表现出来的许多常见方式. 在设计测试时,请花一些时间来回顾已知的测试启发法,以告知我们的测试设计. 我们可以在” 测试工程”部分的”手册”中找到一些有用的启发式方法.
Test speed
GitLab has a massive test suite that, without parallelization, can take hours to run. It’s important that we make an effort to write tests that are accurate and effective 以及 fast.
关于测试性能,需要牢记以下几点:
instance_double
和spy
比FactoryBot.build(...)
快FactoryBot.build(...)
和.build_stubbed
比.create
更快.- 当
build
,build_stubbed
,attributes_for
,spy
或instance_double
可以使用时,不要create
对象. 数据库持久性很慢! - 除非实际需要测试有效,否则不要将功能标记为需要 JavaScript(通过 RSpec 中的
:js
). 无头浏览器测试很慢!
RSpec
要运行 RSpec 测试:
# run test for a file
bin/rspec spec/models/project_spec.rb
# run test for the example on line 10 on that file
bin/rspec spec/models/project_spec.rb:10
# run tests matching the example name has that string
bin/rspec spec/models/project_spec.rb -e associations
# run all tests, will take hours for GitLab codebase!
bin/rspec
使用Guard连续监视更改,并仅运行匹配的测试:
bundle exec guard
一起使用 spring 和 guard 时,请改用SPRING=1 bundle exec guard
来使用 spring.
使用Factory Doctor查找不必要的数据库操作案例,这可能会导致测试缓慢.
# run test for path
FDOC=1 bin/rspec spec/[path]/[to]/[spec].rb
General guidelines
- 使用单个顶级
RSpec.describe ClassName
块. - 使用
.method
来描述类方法,并使用#method
来描述实例方法. - 使用
context
测试分支逻辑. - 尝试使测试的顺序与类中的顺序匹配.
- 尝试遵循四阶段测试模式,使用换行符分隔各个阶段.
- 使用
Gitlab.config.gitlab.host
而不是硬编码'localhost'
- 不要断言序列生成的属性的绝对值(请参见Gotchas ).
- 避免使用
expect_any_instance_of
或allow_any_instance_of
(见陷阱 ). - 不要将
:each
参数提供给钩子,因为它是默认值. - 在钩子
before
和after
,最好将它的作用域设置为:context
不是:all
- 当使用作用在给定元素上的
execute_script
evaluate_script("$('.js-foo').testSomething()")
(或execute_script
)时,请事先使用 Capybara 匹配器(例如find('.js-foo')
)以确保该元素实际存在. - 使用
focus: true
可以隔离要运行的部分规范. - 如果测试中有多个期望,请使用
:aggregate_failures
. - 对于空的测试描述块 ,如果测试是不言自明的,请使用
specify
而不是it do
. - 当您需要一个实际上不存在的 ID / IID /访问级别时,请使用
non_existing_record_id
/non_existing_record_iid
/non_existing_record_access_level
. 使用 123、1234 甚至 999 都很容易,因为在 CI 运行的情况下,这些 ID 实际上可能存在于数据库中.
Coverage
simplecov
用于生成代码测试覆盖率报告. 它们是在 CI 上自动生成的,但在本地运行测试时不会自动生成. 要在计算机上运行规格文件时生成部分报告,请设置SIMPLECOV
环境变量:
SIMPLECOV=1 bundle exec rspec spec/models/repository_spec.rb
覆盖率报告会生成到应用程序根目录下的coverage
文件夹中,您可以在浏览器中打开这些报告,例如:
firefox coverage/index.html
使用覆盖率报告来确保您的测试覆盖 100%的代码.
System / Feature tests
注意:在编写新的系统测试之前, 请考虑不要编写一个 !
- Feature specs should be named
ROLE_ACTION_spec.rb
, such asuser_changes_password_spec.rb
. - 使用描述成功和失败路径的方案标题.
- 避免方案标题未添加任何信息,例如”成功”.
- 避免场景标题重复功能标题.
- Create only the necessary records in the database
- 测试一条幸福的道路和一条不太幸福的道路,仅此而已
- 所有其他可能的路径均应通过单元测试或集成测试进行测试
- 测试页面上显示的内容,而不是 ActiveRecord 模型的内部. 例如,如果您要验证是否已创建记录,请添加期望其记录显示在页面上的期望,而不是
Model.count
增加一. - 可以查找 DOM 元素,但不要滥用它,因为它会使测试更加脆弱
Debugging Capybara
有时您可能需要通过观察浏览器行为来调试 Capybara 测试.
Live debug
您可以使用规范中的live_debug
方法暂停 Capybara 并在浏览器中查看网站. 当前页面将在默认浏览器中自动打开. 您可能需要先登录(当前用户的凭据显示在终端中).
要恢复测试运行,请按任意键.
例如:
$ bin/rspec spec/features/auto_deploy_spec.rb:34
Running via Spring preloader in process 8999
Run options: include {:locations=>{"./spec/features/auto_deploy_spec.rb"=>[34]}}
Current example is paused for live debugging
The current user credentials are: user2 / 12345678
Press any key to resume the execution of the example!
Back to the example!
.
Finished in 34.51 seconds (files took 0.76702 seconds to load)
1 example, 0 failures
注意: live_debug
仅在启用 JavaScript 的规范上起作用.
Run :js
spec in a visible browser
使用CHROME_HEADLESS=0
运行规范,例如:
CHROME_HEADLESS=0 bin/rspec some_spec.rb
测试将很快进行,但这将使您对正在发生的事情有所了解. 对CHROME_HEADLESS=0
使用live_debug
暂停打开的浏览器,并且不会再次打开该页面. 这可用于调试和检查元素.
您还可以添加byebug
或binding.pry
暂停执行,并步通过测试.
Screenshots
我们使用capybara-screenshot
gem 在失败时自动截屏. 在 CI 中,您可以下载这些文件作为作业工件.
另外,您可以通过添加以下方法在测试中的任何时候手动获取屏幕截图. 请确保在不再需要它们时将其删除! 有关更多信息,请参见https://github.com/mattheworiordan/capybara-screenshot#manual-screenshots .
在:js
规范中添加screenshot_and_save_page
以截图 Capybara 看到的内容,并保存页面源代码.
在:js
规范中添加screenshot_and_open_image
以截图 Capybara 看到的内容,并自动打开图像.
由此创建的 HTML 转储缺少 CSS. 这导致它们看起来与实际应用程序截然不同. 有一个添加 CSS 的小技巧 ,可简化调试过程.
Fast unit tests
某些类与 Rails 完全隔离,您应该能够测试它们,而不会因 Rails 环境和 Bundler 的:default
组的 gem 加载而增加开销. 在这些情况下,您可以在测试文件中require 'fast_spec_helper'
而不是require 'spec_helper'
,并且由于以下原因,测试应该运行得非常快:
- 宝石加载被跳过
- Rails 应用启动被跳过
- 跳过了 GitLab Shell 和 Gitaly 设置
- 测试存储库设置被跳过
fast_spec_helper
还支持lib/
目录中的自动加载类. 这意味着只要您的类/模块仅使用lib/
目录中的代码,就无需显式加载任何依赖项. fast_spec_helper
还将加载所有 ActiveSupport 扩展,包括 Rails 环境中常用的核心扩展.
请注意,在某些情况下,当代码使用 gems 或lib/
没有依赖项时,您可能仍必须使用require_dependency
加载某些依赖项.
例如,如果要测试调用Gitlab::UntrustedRegexp
类的代码,该类在Gitlab::UntrustedRegexp
使用re2
库,则应将require_dependency 're2'
添加到需要re2
gem 的库文件中,以达到此要求明确,也可以将其添加到规范本身中,但最好使用前者.
加载使用fast_spec_helper
测试大约需要一秒钟,而不是常规spec_helper
的 30+秒.
let
variables
GitLab 的 RSpec 套件广泛使用let
(以及严格的非延迟版本let!
)变量来减少重复. 但是,有时这有时要以清楚为代价 ,因此我们需要为以后的使用设置一些准则:
let!
变量优于实例变量.let
变量比let!
可取let!
变量. 局部变量比let
变量更可取.- 使用
let
可以减少整个规格文件中的重复项. - 不要使用
let
定义单个测试使用的变量; 在测试的it
块中将它们定义为局部变量. - 不要在顶层
describe
块中定义只用于更深层嵌套context
或describe
块的let
变量. 使定义尽可能靠近使用位置. - 尽量避免将一个
let
变量的定义覆盖另一个. - 不要定义只供另一个定义使用的
let
变量. 请改用辅助方法. let!
变量应该只在需要与定义为了严格评估的情况下使用,否则let
就足够了. 请记住,let
是惰性的,在被引用之前不会被评估.
Common test setup
在某些情况下,无需为每个示例再次创建相同的对象以进行测试. 例如,需要一个项目和该项目的访客来测试同一项目上的问题,一个项目和一个用户将对整个文件进行测试.
尽可能不要使用before(:all)
或before(:context)
实现此目的. 如果这样做,您将需要手动清理数据,因为这些挂钩在数据库事务外部运行.
相反,这可以通过使用来实现let_it_be
变量和before_all
钩从test-prof
的宝石 .
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
before_all do
project.add_guest(user)
end
这将仅为此上下文创建一个Project
, User
和ProjectMember
.
let_it_be
和before_all
在嵌套上下文中也可用. 使用事务回滚自动处理上下文后进行清理.
Note that if you modify an object defined inside a let_it_be
block, then you will need to reload the object as needed, or specify the reload
option to reload for every example.
let_it_be(:project, reload: true) { create(:project) }
您还可以指定refind
选项以完全加载新对象.
let_it_be(:project, refind: true) { create(:project) }
Time-sensitive tests
在基于 Ruby 的测试中可以使用Timecop来验证对时间敏感的事物. 任何锻炼或验证时间敏感的测试都应使用 Timecop 来防止瞬态测试失败.
Example:
it 'is overdue' do
issue = build(:issue, due_date: Date.tomorrow)
Timecop.freeze(3.days.from_now) do
expect(issue).to be_overdue
end
end
Feature flags in tests
在基于 Ruby 的测试中,默认情况下所有功能标志都已启用.
要在测试中禁用功能标记,请使用stub_feature_flags
帮助器. 例如,要在测试中全局禁用ci_live_trace
功能标志:
stub_feature_flags(ci_live_trace: false)
Feature.enabled?(:ci_live_trace) # => false
如果您希望设置一个仅对某些角色而不对其他角色启用功能标志的测试,则可以在传递给助手的选项中指定此功能. 例如,要为特定项目启用ci_live_trace
功能标记:
project1, project2 = build_list(:project, 2)
# Feature will only be enabled for project1
stub_feature_flags(ci_live_trace: project1)
Feature.enabled?(:ci_live_trace) # => false
Feature.enabled?(:ci_live_trace, project1) # => true
Feature.enabled?(:ci_live_trace, project2) # => false
这代表了 FlipperGate 的实际行为:
- 您可以启用要启用的指定角色的替代
- 您可以禁用(删除)指定角色的替代,并还原为默认状态
- 无法建模您明确禁用指定的参与者
Feature.enable(:my_feature)
Feature.disable(:my_feature, project1)
Feature.enabled?(:my_feature) # => true
Feature.enabled?(:my_feature, project1) # => true
Feature.disable(:my_feature2)
Feature.enable(:my_feature2, project1)
Feature.enabled?(:my_feature2) # => false
Feature.enabled?(:my_feature2, project1) # => true
stub_feature_flags
vs Feature.enable*
最好使用stub_feature_flags
在测试环境中启用功能标志. 该方法为简单的用例提供了一个简单且描述良好的界面.
但是,在某些情况下,需要测试更复杂的行为,例如功能标志百分比展示. 这可以使用.enable_percentage_of_time
和.enable_percentage_of_actors
来实现
# Good: feature needs to be explicitly disabled, as it is enabled by default if not defined
stub_feature_flags(my_feature: false)
stub_feature_flags(my_feature: true)
stub_feature_flags(my_feature: project)
stub_feature_flags(my_feature: [project, project2])
# Bad
Feature.enable(:my_feature_2)
# Good: enable my_feature for 50% of time
Feature.enable_percentage_of_time(:my_feature_3, 50)
# Good: enable my_feature for 50% of actors/gates/things
Feature.enable_percentage_of_actors(:my_feature_4, 50)
具有定义状态的每个功能标志将在测试执行时间内保留:
Feature.persisted_names.include?('my_feature') => true
Feature.persisted_names.include?('my_feature_2') => true
Feature.persisted_names.include?('my_feature_3') => true
Feature.persisted_names.include?('my_feature_4') => true
Stubbing gate
要求将作为参数传递给Feature.enabled?
和Feature.disabled?
是包含FeatureGate
的对象.
在规范中,您可以使用stub_feature_flag_gate
方法,该方法可让您快速使用自定义门:
gate = stub_feature_flag_gate('CustomActor')
stub_feature_flags(ci_live_trace: gate)
Feature.enabled?(:ci_live_trace) # => false
Feature.enabled?(:ci_live_trace, gate) # => true
Pristine test environments
单个 GitLab 测试执行的代码可以访问和修改许多数据项. 无需在测试运行之前进行仔细的准备,然后再进行清理,则测试可以更改数据,从而影响后续测试的行为. 应不惜一切代价避免这种情况! 幸运的是,现有的测试框架已经可以处理大多数情况.
当测试环境确实受到污染时,常见的结果就是不稳定的测试 . 污染通常表现为顺序依赖性:运行规格 A,然后运行规格 B 会可靠地失败,但是运行规格 B,然后运行规格 A 将可靠地成功. 在这些情况下,可以使用rspec --bisect
(或规格文件的手动成对二rspec --bisect
)来确定哪个规格有问题. 要解决此问题,需要对测试套件如何确保原始环境有一些了解. 继续阅读以发现有关每个数据存储的更多信息!
SQL database
这是由database_cleaner
gem 为我们管理的. 每个规范都包含在一个事务中,一旦测试完成,该事务将回滚. 某些规范将在完成后针对每个表发出DELETE FROM
查询; 这样可以从多个数据库连接中查看创建的行,这对于在浏览器中运行的规范或迁移规范等非常重要.
使用这些策略而不是众所周知的TRUNCATE TABLES
方法的结果是,主键和其他序列不会在整个规范中重置. 因此,如果您在规范 A 中创建一个项目,然后在规范 B 中创建一个项目,则第一个将具有id=1
,而第二个将具有id=2
.
这意味着规范永远不应依赖 ID 或任何其他序列生成的列的值. 为避免意外冲突,规范还应避免手动指定此类列中的任何值. 而是将它们保留为未指定状态,并在创建行后查找值.
Redis
GitLab 在 Redis 中存储了两个主要的数据类别:缓存项和 Sidekiq 作业.
在大多数规范中,Rails 缓存实际上是一个内存存储. 规格之间已替换了该代码,因此对Rails.cache.read
和Rails.cache.write
调用是安全的. 但是,如果某个规范直接进行 Redis 调用,则应适当地使用:clean_gitlab_redis_cache
, :clean_gitlab_redis_shared_state
或:clean_gitlab_redis_queues
特征对其进行标记.
Background jobs / Sidekiq
默认情况下,Sidekiq 作业会排队到作业数组中,并且不会进行处理. 如果测试将 Sidekiq 作业排入队列并需要对其进行处理,则可以使用:sidekiq_inline
特性.
The :sidekiq_might_not_need_inline
trait was added when Sidekiq inline mode was changed to fake mode to all the tests that needed Sidekiq to actually process jobs. Tests with this trait should be either fixed to not rely on Sidekiq processing jobs, or their :sidekiq_might_not_need_inline
trait should be updated to :sidekiq_inline
if the processing of background jobs is needed/expected.
注意: perform_enqueued_jobs
的用法仅对测试延迟的邮件传递有用,因为我们的 Sidekiq 工作者不是从ApplicationJob
/ ActiveJob::Base
继承.
DNS
DNS 请求在测试套件中普遍存在(截至!22368 ),因为 DNS 可能会导致问题,具体取决于开发人员的本地网络. 在spec/support/dns.rb
有可用的 RSpec 标签,如果您需要绕过 DNS 存根,则可以将其应用于测试,例如:
it "really connects to Prometheus", :permit_dns do
而且,如果需要更具体的控制,则可以在spec/support/helpers/dns_helpers.rb
实现 DNS 阻止,并且可以在其他地方调用这些方法.
Filesystem
文件系统数据可以大致分为”存储库”和”其他所有内容”. 存储库存储在tmp/tests/repositories
. 在测试运行开始之前和测试运行结束之后,将清空此目录. 在规格之间不清空它,因此在过程的整个生命周期中,创建的存储库都在此目录中累积. 删除它们很昂贵,但是除非精心管理,否则可能会导致污染.
为避免这种情况,在测试套件中启用了哈希存储 . 这意味着将为存储库提供一个唯一的路径,该路径取决于其项目的 ID. 由于在规范之间未重置项目 ID,因此可以确保每个规范在磁盘上获得自己的存储库,并防止在规范之间可见更改.
如果规范手动指定了项目 ID,或直接检查tmp/tests/repositories/
目录的状态,则它应该在运行之前和之后清理该目录. 通常,应完全避免这些模式.
链接到数据库对象的其他文件类别(例如上载)通常以相同的方式进行管理. 在规范中启用了散列存储后,它们将在 ID 确定的位置写入磁盘,因此不会发生冲突.
一些规范通过将:legacy_storage
特征传递给projects
工厂来禁用哈希存储. 执行此操作的规范绝不能覆盖项目或其任何组的path
. 默认路径包含项目 ID,因此不会发生冲突. 但是,如果两个规范创建具有相同路径的:legacy_storage
项目,则它们将使用磁盘上的相同存储库并导致测试环境污染.
其他文件必须由规范手动管理. 例如,如果运行创建tmp/test-file.csv
文件的代码,则规范必须确保在清理过程中删除了该文件.
Persistent in-memory application state
给定rspec
运行中的所有规范共享相同的 Ruby 进程,这意味着它们可以通过修改规范之间可访问的 Ruby 对象相互影响. 实际上,这意味着全局变量和常量(包括 Ruby 类,模块等).
全局变量通常不应修改. 如果绝对必要,则可以使用如下所示的块来确保更改被回滚:
around(:each) do |example|
old_value = $0
begin
$0 = "new-value"
example.run
ensure
$0 = old_value
end
end
如果规范需要修改常量,则应使用stub_const
帮助器以确保更改被回滚.
如果需要修改ENV
常量的内容,则可以改用stub_env
帮助器方法.
虽然大多数 Ruby 实例不在规范之间共享,但类和模块通常是共享的. 类和模块实例变量,访问器,类变量和其他有状态习语应与全局变量一样对待-除非必须修改,否则请勿对其进行修改! 特别是,最好使用期望值或依赖项注入以及存根,以避免进行修改. 如果没有其他选择,可以使用类似于上面的全局变量示例的around
块,但应尽可能避免这种情况.
Table-based / Parameterized tests
这种测试风格用于执行一段具有广泛输入范围的代码. 通过一次指定测试用例,并在每个输入和预期输出表的旁边,可以使您的测试更易于阅读和紧凑.
我们使用RSpec :: Parameterized gem. 一个简短的示例,使用表语法并检查 Ruby 相等性是否为一系列输入,可能看起来像这样:
describe "#==" do
using RSpec::Parameterized::TableSyntax
where(:a, :b, :result) do
1 | 1 | true
1 | 2 | false
true | true | true
true | false | false
end
with_them do
it { expect(a == b).to eq(result) }
it 'is isomorphic' do
expect(b == a).to eq(result)
end
end
end
注意:仅将简单值用作where
块中的输入. 使用 proc,有状态对象,FactoryBot 创建的对象等可能导致意外结果 .
Prometheus tests
可以将 Prometheus 度量从一次测试运行保存到另一次测试. 为了确保在每个示例之前重置指标,请在 RSpec 测试中添加:prometheus
标记.
Matchers
应该创建自定义匹配器以阐明意图和/或隐藏 RSpec 期望的复杂性. 它们应放在spec/support/matchers/
. 如果匹配器仅适用于某种类型的规范(例如功能,请求等),则可以放在子文件夹中,但如果它们适用于多种类型的规范,则不应该放在子文件夹中.
be_like_time
从数据库返回的时间的精度可能与 Ruby 中的时间对象不同,因此在规格比较时我们需要灵活的容差. 我们可以使用be_like_time
来比较时间在彼此之间一秒之内.
Example:
expect(metrics.merged_at).to be_like_time(time)
have_gitlab_http_status
have_gitlab_http_status
使用have_http_status
而不是have_http_status
和have_http_status
expect(response.status).to
have_http_status
,因为只要状态不匹配,前者还可以显示响应主体. 每当某些测试开始中断时,这将非常有用,我们很想知道为什么不编辑源代码并重新运行测试.
每当它显示 500 内部服务器错误时,此功能特别有用.
在数字表示形式206
:no_content
命名为 HTTP 的状态,如:no_content
. 请参阅支持的状态代码列表.
Example:
expect(response).to have_gitlab_http_status(:ok)
Testing query performance
测试查询性能使我们能够:
- 断言代码块中不存在 N + 1 个问题.
- 确保代码块中的查询数量不会被忽略.
QueryRecorder
QueryRecorder
允许分析和测试在给定代码块中执行的数据库查询的数量.
有关更多详细信息,请参见QueryRecorder
部分.
GitalyClient
Gitlab::GitalyClient.get_request_count
allows tests of the number of Gitaly queries made by a given block of code:
有关更多详细信息,请参见Gitaly Request Counts
部分.
Shared contexts
可以内联声明仅在一个规范文件中使用的共享上下文. 一个以上规范文件使用的任何共享上下文:
- 应该放在
spec/support/shared_contexts/
. - 如果它们仅适用于某种类型的规范(例如功能,请求等),则可以放在子文件夹中,但如果它们适用于多种类型的规范,则不应放在子文件夹中.
每个文件应仅包含一个上下文并具有描述性名称,例如spec/support/shared_contexts/controllers/githubish_import_controller_shared_context.rb
.
Shared examples
可以内联声明仅在一个规范文件中使用的共享示例. 多个规范文件使用的任何共享示例:
- 应该放在
spec/support/shared_examples/
. - 如果它们仅适用于某种类型的规范(例如功能,请求等),则可以放在子文件夹中,但如果它们适用于多种类型的规范,则不应放在子文件夹中.
每个文件应仅包含一个上下文并具有描述性名称,例如spec/support/shared_examples/controllers/githubish_import_controller_shared_example.rb
.
Helpers
帮助程序通常是提供一些隐藏特定 RSpec 示例复杂性的方法的模块. 如果不希望与其他规范共享,则可以在 RSpec 文件中定义帮助程序. 否则,应将它们放在spec/support/helpers/
. 如果助手仅适用于某种类型的规范(例如功能,请求等),则可以放在子文件夹中,但如果它们适用于多种类型的规范,则不应放在子文件夹中.
助手应遵循 Rails 的命名/命名空间约定. 例如spec/support/helpers/cycle_analytics_helpers.rb
应该定义:
module Spec
module Support
module Helpers
module CycleAnalyticsHelpers
def create_commit_referencing_issue(issue, branch_name: random_git_name)
project.repository.add_branch(user, branch_name, 'master')
create_commit("Commit for ##{issue.iid}", issue.project, user, branch_name)
end
end
end
end
end
助手不应更改 RSpec 配置. 例如,上述帮助器模块不应包括:
RSpec.configure do |config|
config.include Spec::Support::Helpers::CycleAnalyticsHelpers
end
Factories
GitLab 使用factory_bot替代测试夹具.
- 工厂定义存在于
spec/factories/
,使用其对应模型的复数形式命名(User
工厂在users.rb
中定义). - 每个文件应该只有一个顶级工厂定义.
- FactoryBot 方法混入了所有 RSpec 组. 这意味着您可以(并且应该)调用
create(...)
而不是FactoryBot.create(...)
. - 利用特征来清理定义和用法.
- 定义工厂时,请勿定义结果记录通过验证不需要的属性.
- 从工厂实例化时,不要提供测试不需要的属性.
- 工厂不必仅限于
ActiveRecord
对象. 参见示例 .
Fixtures
所有固定装置应放置在spec/fixtures/
.
Repositories
测试某些功能(例如,合并合并请求)需要在测试环境中具有某种状态的 Git 存储库. GitLab 在某些常见情况下会维护gitlab-test
存储库-您可以确保该存储库的副本与:repository
特性一起用于项目工厂:
let(:project) { create(:project, :repository) }
可以的话,请考虑使用:custom_repo
特性而不是:repository
. 这使您可以精确地指定哪些文件将出现在项目存储库的master
分支中. 例如:
let(:project) do
create(
:project, :custom_repo,
files: {
'README.md' => 'Content here',
'foo/bar/baz.txt' => 'More content here'
}
)
end
这将创建一个包含两个文件的存储库,这两个文件具有默认权限和指定的内容.
Configuration
RSpec 配置文件是更改 RSpec 配置的文件(即RSpec.configure do |config|
块). 它们应放在spec/support/
.
每个文件都应与特定域相关,例如spec/support/capybara.rb
, spec/support/carrierwave.rb
等.
如果 helpers 模块仅适用于某种规格,则应在config.include
调用中添加修饰符. 例如,如果spec/support/helpers/cycle_analytics_helpers.rb
仅适用于:lib
和type: :model
specs,则应编写以下内容:
RSpec.configure do |config|
config.include Spec::Support::Helpers::CycleAnalyticsHelpers, :lib
config.include Spec::Support::Helpers::CycleAnalyticsHelpers, type: :model
end
If a configuration file only consists of config.include
, you can add these config.include
directly in spec/spec_helper.rb
.
对于非常通用的帮助程序,请考虑将它们包括在spec/support/rspec.rb
文件使用的spec/fast_spec_helper.rb
文件中. 有关spec/fast_spec_helper.rb
文件的更多详细信息,请参见快速单元测试 .
Test environment logging
在运行测试时,会自动配置并启动用于测试环境的服务,包括 Gitaly,Workhorse,Elasticsearch 和 Capybara. 在 CI 中运行时,或者如果需要安装服务,则测试环境将记录有关设置时间的信息,并产生如下日志消息:
==> Setting up Gitaly...
Gitaly set up in 31.459649 seconds...
==> Setting up GitLab Workhorse...
GitLab Workhorse set up in 29.695619 seconds...
fatal: update refs/heads/diff-files-symlink-to-image: invalid <newvalue>: 8cfca84
From https://gitlab.com/gitlab-org/gitlab-test
* [new branch] diff-files-image-to-symlink -> origin/diff-files-image-to-symlink
* [new branch] diff-files-symlink-to-image -> origin/diff-files-symlink-to-image
* [new branch] diff-files-symlink-to-text -> origin/diff-files-symlink-to-text
* [new branch] diff-files-text-to-symlink -> origin/diff-files-text-to-symlink
b80faa8..40232f7 snippet/multiple-files -> origin/snippet/multiple-files
* [new branch] testing/branch-with-#-hash -> origin/testing/branch-with-#-hash
==> Setting up GitLab Elasticsearch Indexer...
GitLab Elasticsearch Indexer set up in 26.514623 seconds...
当在本地运行并且不需要执行任何操作时,将忽略此信息. 如果您始终希望看到这些消息,请设置以下环境变量:
GITLAB_TESTING_LOG_LEVEL=debug