diff options
Diffstat (limited to 'doc/development/testing_guide/best_practices.md')
-rw-r--r-- | doc/development/testing_guide/best_practices.md | 83 |
1 files changed, 72 insertions, 11 deletions
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md index f0137e542cc..7bb8473117f 100644 --- a/doc/development/testing_guide/best_practices.md +++ b/doc/development/testing_guide/best_practices.md @@ -35,11 +35,18 @@ Here are some things to keep in mind regarding test performance: To run RSpec tests: ```shell -# run all tests +# 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 -# run test for path -bin/rspec spec/[path]/[to]/[spec].rb ``` Use [Guard](https://github.com/guard/guard) to continuously monitor for changes and only run matching tests: @@ -59,7 +66,7 @@ FDOC=1 bin/rspec spec/[path]/[to]/[spec].rb ### General guidelines -- Use a single, top-level `describe ClassName` block. +- Use a single, top-level `RSpec.describe ClassName` block. - Use `.method` to describe class methods and `#method` to describe instance methods. - Use `context` to test branching logic. @@ -323,7 +330,7 @@ Feature.enabled?(:ci_live_trace) # => false If you wish to set up a test where a feature flag is enabled only for some actors and not others, you can specify this in options passed to the helper. For example, to enable the `ci_live_trace` -feature flag for a specifc project: +feature flag for a specific project: ```ruby project1, project2 = build_list(:project, 2) @@ -340,7 +347,7 @@ This represents an actual behavior of FlipperGate: 1. You can enable an override for a specified actor to be enabled 1. You can disable (remove) an override for a specified actor, - fallbacking to default state + falling back to default state 1. There's no way to model that you explicitly disable a specified actor ```ruby @@ -357,6 +364,60 @@ Feature.enabled?(:my_feature2) # => false Feature.enabled?(:my_feature2, project1) # => true ``` +#### `stub_feature_flags` vs `Feature.enable*` + +It is preferred to use `stub_feature_flags` for enabling feature flags +in testing environment. This method provides a simple and well described +interface for a simple use-cases. + +However, in some cases a more complex behaviors needs to be tested, +like a feature flag percentage rollouts. This can be achieved using +the `.enable_percentage_of_time` and `.enable_percentage_of_actors` + +```ruby +# 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) +``` + +Each feature flag that has a defined state will be persisted +for test execution time: + +```ruby +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 + +It is required that a gate that is passed as an argument to `Feature.enabled?` +and `Feature.disabled?` is an object that includes `FeatureGate`. + +In specs you can use a `stub_feature_flag_gate` method that allows you to have +quickly your custom gate: + +```ruby +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 The code exercised by a single GitLab test may access and modify many items of @@ -406,7 +467,7 @@ However, if a spec makes direct Redis calls, it should mark itself with the #### Background jobs / Sidekiq By default, Sidekiq jobs are enqueued into a jobs array and aren't processed. -If a test enqueues Sidekiq jobs and need them to be processed, the +If a test queues Sidekiq jobs and need them to be processed, the `:sidekiq_inline` trait can be used. The `:sidekiq_might_not_need_inline` trait was added when [Sidekiq inline mode was @@ -662,7 +723,7 @@ module Spec end ``` -Helpers should not change the RSpec config. For instance, the helpers module +Helpers should not change the RSpec configuration. For instance, the helpers module described above should not include: ```ruby @@ -723,9 +784,9 @@ end This will create a repository containing two files, with default permissions and the specified content. -### Config +### Configuration -RSpec config files are files that change the RSpec config (i.e. +RSpec configuration files are files that change the RSpec configuration (i.e. `RSpec.configure do |config|` blocks). They should be placed under `spec/support/`. @@ -744,7 +805,7 @@ RSpec.configure do |config| end ``` -If a config file only consists of `config.include`, you can add these +If a configuration file only consists of `config.include`, you can add these `config.include` directly in `spec/spec_helper.rb`. For very generic helpers, consider including them in the `spec/support/rspec.rb` |