Testing best practices

原文:https://docs.gitlab.com/ee/development/testing_guide/best_practices.html

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_doublespyFactoryBot.build(...)
  • FactoryBot.build(...).build_stubbed.create更快.
  • buildbuild_stubbedattributes_forspyinstance_double可以使用时,不要create对象. 数据库持久性很慢!
  • 除非实际需要测试有效,否则不要将功能标记为需要 JavaScript(通过 RSpec 中的:js ). 无头浏览器测试很慢!

RSpec

要运行 RSpec 测试:

  1. # run test for a file
  2. bin/rspec spec/models/project_spec.rb
  3. # run test for the example on line 10 on that file
  4. bin/rspec spec/models/project_spec.rb:10
  5. # run tests matching the example name has that string
  6. bin/rspec spec/models/project_spec.rb -e associations
  7. # run all tests, will take hours for GitLab codebase!
  8. bin/rspec

使用Guard连续监视更改,并仅运行匹配的测试:

  1. bundle exec guard

一起使用 spring 和 guard 时,请改用SPRING=1 bundle exec guard来使用 spring.

使用Factory Doctor查找不必要的数据库操作案例,这可能会导致测试缓慢.

  1. # run test for path
  2. 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_ofallow_any_instance_of (见陷阱 ).
  • 不要将:each参数提供给钩子,因为它是默认值.
  • 在钩子beforeafter ,最好将它的作用域设置为: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环境变量:

  1. SIMPLECOV=1 bundle exec rspec spec/models/repository_spec.rb

覆盖率报告会生成到应用程序根目录下的coverage文件夹中,您可以在浏览器中打开这些报告,例如:

  1. firefox coverage/index.html

使用覆盖率报告来确保您的测试覆盖 100%的代码.

System / Feature tests

注意:在编写新的系统测试之前, 请考虑不要编写一个

  • Feature specs should be named ROLE_ACTION_spec.rb, such as user_changes_password_spec.rb.
  • 使用描述成功和失败路径的方案标题.
  • 避免方案标题未添加任何信息,例如”成功”.
  • 避免场景标题重复功能标题.
  • Create only the necessary records in the database
  • 测试一条幸福的道路和一条不太幸福的道路,仅此而已
  • 所有其他可能的路径均应通过单元测试或集成测试进行测试
  • 测试页面上显示的内容,而不是 ActiveRecord 模型的内部. 例如,如果您要验证是否已创建记录,请添加期望其记录显示在页面上的期望,而不是Model.count增加一.
  • 可以查找 DOM 元素,但不要滥用它,因为它会使测试更加脆弱

Debugging Capybara

有时您可能需要通过观察浏览器行为来调试 Capybara 测试.

Live debug

您可以使用规范中的live_debug方法暂停 Capybara 并在浏览器中查看网站. 当前页面将在默认浏览器中自动打开. 您可能需要先登录(当前用户的凭据显示在终端中).

要恢复测试运行,请按任意键.

例如:

  1. $ bin/rspec spec/features/auto_deploy_spec.rb:34
  2. Running via Spring preloader in process 8999
  3. Run options: include {:locations=>{"./spec/features/auto_deploy_spec.rb"=>[34]}}
  4. Current example is paused for live debugging
  5. The current user credentials are: user2 / 12345678
  6. Press any key to resume the execution of the example!
  7. Back to the example!
  8. .
  9. Finished in 34.51 seconds (files took 0.76702 seconds to load)
  10. 1 example, 0 failures

注意: live_debug仅在启用 JavaScript 的规范上起作用.

Run :js spec in a visible browser

使用CHROME_HEADLESS=0运行规范,例如:

  1. CHROME_HEADLESS=0 bin/rspec some_spec.rb

测试将很快进行,但这将使您对正在发生的事情有所了解. 对CHROME_HEADLESS=0使用live_debug暂停打开的浏览器,并且不会再次打开该页面. 这可用于调试和检查元素.

您还可以添加byebugbinding.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块中定义只用于更深层嵌套contextdescribe块的let变量. 使定义尽可能靠近使用位置.
  • 尽量避免将一个let变量的定义覆盖另一个.
  • 不要定义只供另一个定义使用的let变量. 请改用辅助方法.
  • let! 变量应该只在需要与定义为了严格评估的情况下使用,否则let就足够了. 请记住, let是惰性的,在被引用之前不会被评估.

Common test setup

在某些情况下,无需为每个示例再次创建相同的对象以进行测试. 例如,需要一个项目和该项目的访客来测试同一项目上的问题,一个项目和一个用户将对整个文件进行测试.

尽可能不要使用before(:all)before(:context)实现此目的. 如果这样做,您将需要手动清理数据,因为这些挂钩在数据库事务外部运行.

相反,这可以通过使用来实现let_it_be变量和before_all钩从test-prof的宝石 .

  1. let_it_be(:project) { create(:project) }
  2. let_it_be(:user) { create(:user) }
  3. before_all do
  4. project.add_guest(user)
  5. end

这将仅为此上下文创建一个ProjectUserProjectMember .

let_it_bebefore_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.

  1. let_it_be(:project, reload: true) { create(:project) }

您还可以指定refind选项以完全加载新对象.

  1. let_it_be(:project, refind: true) { create(:project) }

Time-sensitive tests

在基于 Ruby 的测试中可以使用Timecop来验证对时间敏感的事物. 任何锻炼或验证时间敏感的测试都应使用 Timecop 来防止瞬态测试失败.

Example:

  1. it 'is overdue' do
  2. issue = build(:issue, due_date: Date.tomorrow)
  3. Timecop.freeze(3.days.from_now) do
  4. expect(issue).to be_overdue
  5. end
  6. end

Feature flags in tests

在基于 Ruby 的测试中,默认情况下所有功能标志都已启用.

要在测试中禁用功能标记,请使用stub_feature_flags帮助器. 例如,要在测试中全局禁用ci_live_trace功能标志:

  1. stub_feature_flags(ci_live_trace: false)
  2. Feature.enabled?(:ci_live_trace) # => false

如果您希望设置一个仅对某些角色而不对其他角色启用功能标志的测试,则可以在传递给助手的选项中指定此功能. 例如,要为特定项目启用ci_live_trace功能标记:

  1. project1, project2 = build_list(:project, 2)
  2. # Feature will only be enabled for project1
  3. stub_feature_flags(ci_live_trace: project1)
  4. Feature.enabled?(:ci_live_trace) # => false
  5. Feature.enabled?(:ci_live_trace, project1) # => true
  6. Feature.enabled?(:ci_live_trace, project2) # => false

这代表了 FlipperGate 的实际行为:

  1. 您可以启用要启用的指定角色的替代
  2. 您可以禁用(删除)指定角色的替代,并还原为默认状态
  3. 无法建模您明确禁用指定的参与者
  1. Feature.enable(:my_feature)
  2. Feature.disable(:my_feature, project1)
  3. Feature.enabled?(:my_feature) # => true
  4. Feature.enabled?(:my_feature, project1) # => true
  1. Feature.disable(:my_feature2)
  2. Feature.enable(:my_feature2, project1)
  3. Feature.enabled?(:my_feature2) # => false
  4. Feature.enabled?(:my_feature2, project1) # => true

stub_feature_flags vs Feature.enable*

最好使用stub_feature_flags在测试环境中启用功能标志. 该方法为简单的用例提供了一个简单且描述良好的界面.

但是,在某些情况下,需要测试更复杂的行为,例如功能标志百分比展示. 这可以使用.enable_percentage_of_time.enable_percentage_of_actors来实现

  1. # Good: feature needs to be explicitly disabled, as it is enabled by default if not defined
  2. stub_feature_flags(my_feature: false)
  3. stub_feature_flags(my_feature: true)
  4. stub_feature_flags(my_feature: project)
  5. stub_feature_flags(my_feature: [project, project2])
  6. # Bad
  7. Feature.enable(:my_feature_2)
  8. # Good: enable my_feature for 50% of time
  9. Feature.enable_percentage_of_time(:my_feature_3, 50)
  10. # Good: enable my_feature for 50% of actors/gates/things
  11. Feature.enable_percentage_of_actors(:my_feature_4, 50)

具有定义状态的每个功能标志将在测试执行时间内保留:

  1. Feature.persisted_names.include?('my_feature') => true
  2. Feature.persisted_names.include?('my_feature_2') => true
  3. Feature.persisted_names.include?('my_feature_3') => true
  4. Feature.persisted_names.include?('my_feature_4') => true

Stubbing gate

要求将作为参数传递给Feature.enabled?Feature.disabled? 是包含FeatureGate的对象.

在规范中,您可以使用stub_feature_flag_gate方法,该方法可让您快速使用自定义门:

  1. gate = stub_feature_flag_gate('CustomActor')
  2. stub_feature_flags(ci_live_trace: gate)
  3. Feature.enabled?(:ci_live_trace) # => false
  4. 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.readRails.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 存根,则可以将其应用于测试,例如:

  1. 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 类,模块等).

全局变量通常不应修改. 如果绝对必要,则可以使用如下所示的块来确保更改被回滚:

  1. around(:each) do |example|
  2. old_value = $0
  3. begin
  4. $0 = "new-value"
  5. example.run
  6. ensure
  7. $0 = old_value
  8. end
  9. end

如果规范需要修改常量,则应使用stub_const帮助器以确保更改被回滚.

如果需要修改ENV常量的内容,则可以改用stub_env帮助器方法.

虽然大多数 Ruby 实例不在规范之间共享,但模块通常是共享的. 类和模块实例变量,访问器,类变量和其他有状态习语应与全局变量一样对待-除非必须修改,否则请勿对其进行修改! 特别是,最好使用期望值或依赖项注入以及存根,以避免进行修改. 如果没有其他选择,可以使用类似于上面的全局变量示例的around块,但应尽可能避免这种情况.

Table-based / Parameterized tests

这种测试风格用于执行一段具有广泛输入范围的代码. 通过一次指定测试用例,并在每个输入和预期输出表的旁边,可以使您的测试更易于阅读和紧凑.

我们使用RSpec :: Parameterized gem. 一个简短的示例,使用表语法并检查 Ruby 相等性是否为一系列输入,可能看起来像这样:

  1. describe "#==" do
  2. using RSpec::Parameterized::TableSyntax
  3. where(:a, :b, :result) do
  4. 1 | 1 | true
  5. 1 | 2 | false
  6. true | true | true
  7. true | false | false
  8. end
  9. with_them do
  10. it { expect(a == b).to eq(result) }
  11. it 'is isomorphic' do
  12. expect(b == a).to eq(result)
  13. end
  14. end
  15. end

注意:仅将简单值用作where块中的输入. 使用 proc,有状态对象,FactoryBot 创建的对象等可能导致意外结果 .

Prometheus tests

可以将 Prometheus 度量从一次测试运行保存到另一次测试. 为了确保在每个示例之前重置指标,请在 RSpec 测试中添加:prometheus标记.

Matchers

应该创建自定义匹配器以阐明意图和/或隐藏 RSpec 期望的复杂性. 它们应放在spec/support/matchers/ . 如果匹配器仅适用于某种类型的规范(例如功能,请求等),则可以放在子文件夹中,但如果它们适用于多种类型的规范,则不应该放在子文件夹中.

be_like_time

从数据库返回的时间的精度可能与 Ruby 中的时间对象不同,因此在规格比较时我们需要灵活的容差. 我们可以使用be_like_time来比较时间在彼此之间一秒之内.

Example:

  1. expect(metrics.merged_at).to be_like_time(time)

have_gitlab_http_status

have_gitlab_http_status使用have_http_status而不是have_http_statushave_http_status expect(response.status).to have_http_status ,因为只要状态不匹配,前者还可以显示响应主体. 每当某些测试开始中断时,这将非常有用,我们很想知道为什么不编辑源代码并重新运行测试.

每当它显示 500 内部服务器错误时,此功能特别有用.

在数字表示形式206 :no_content命名为 HTTP 的状态,如:no_content . 请参阅支持的状态代码列表.

Example:

  1. 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应该定义:

  1. module Spec
  2. module Support
  3. module Helpers
  4. module CycleAnalyticsHelpers
  5. def create_commit_referencing_issue(issue, branch_name: random_git_name)
  6. project.repository.add_branch(user, branch_name, 'master')
  7. create_commit("Commit for ##{issue.iid}", issue.project, user, branch_name)
  8. end
  9. end
  10. end
  11. end
  12. end

助手不应更改 RSpec 配置. 例如,上述帮助器模块不应包括:

  1. RSpec.configure do |config|
  2. config.include Spec::Support::Helpers::CycleAnalyticsHelpers
  3. 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特性一起用于项目工厂:

  1. let(:project) { create(:project, :repository) }

可以的话,请考虑使用:custom_repo特性而不是:repository . 这使您可以精确地指定哪些文件将出现在项目存储库的master分支中. 例如:

  1. let(:project) do
  2. create(
  3. :project, :custom_repo,
  4. files: {
  5. 'README.md' => 'Content here',
  6. 'foo/bar/baz.txt' => 'More content here'
  7. }
  8. )
  9. end

这将创建一个包含两个文件的存储库,这两个文件具有默认权限和指定的内容.

Configuration

RSpec 配置文件是更改 RSpec 配置的文件(即RSpec.configure do |config|块). 它们应放在spec/support/ .

每个文件都应与特定域相关,例如spec/support/capybara.rbspec/support/carrierwave.rb等.

如果 helpers 模块仅适用于某种规格,则应在config.include调用中添加修饰符. 例如,如果spec/support/helpers/cycle_analytics_helpers.rb仅适用于:libtype: :model specs,则应编写以下内容:

  1. RSpec.configure do |config|
  2. config.include Spec::Support::Helpers::CycleAnalyticsHelpers, :lib
  3. config.include Spec::Support::Helpers::CycleAnalyticsHelpers, type: :model
  4. 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 中运行时,或者如果需要安装服务,则测试环境将记录有关设置时间的信息,并产生如下日志消息:

  1. ==> Setting up Gitaly...
  2. Gitaly set up in 31.459649 seconds...
  3. ==> Setting up GitLab Workhorse...
  4. GitLab Workhorse set up in 29.695619 seconds...
  5. fatal: update refs/heads/diff-files-symlink-to-image: invalid <newvalue>: 8cfca84
  6. From https://gitlab.com/gitlab-org/gitlab-test
  7. * [new branch] diff-files-image-to-symlink -> origin/diff-files-image-to-symlink
  8. * [new branch] diff-files-symlink-to-image -> origin/diff-files-symlink-to-image
  9. * [new branch] diff-files-symlink-to-text -> origin/diff-files-symlink-to-text
  10. * [new branch] diff-files-text-to-symlink -> origin/diff-files-text-to-symlink
  11. b80faa8..40232f7 snippet/multiple-files -> origin/snippet/multiple-files
  12. * [new branch] testing/branch-with-#-hash -> origin/testing/branch-with-#-hash
  13. ==> Setting up GitLab Elasticsearch Indexer...
  14. GitLab Elasticsearch Indexer set up in 26.514623 seconds...

当在本地运行并且不需要执行任何操作时,将忽略此信息. 如果您始终希望看到这些消息,请设置以下环境变量:

  1. GITLAB_TESTING_LOG_LEVEL=debug

Return to Testing documentation