module Gitlab::Experiment::RSpecHelpers

Public Instance Methods

stub_experiments(experiments, times = nil) click to toggle source
# File lib/gitlab/experiment/rspec.rb, line 6
def stub_experiments(experiments, times = nil)
  experiments.each { |experiment| wrapped_experiment(experiment, times) }
end
wrapped_experiment(experiment, times = nil, expected = false) { |e| ... } click to toggle source
# File lib/gitlab/experiment/rspec.rb, line 10
def wrapped_experiment(experiment, times = nil, expected = false, &block)
  klass, experiment_name, variant_name = *experiment_details(experiment)
  base_klass = Configuration.base_class.constantize

  # Set expectations on experiment classes so we can and_wrap_original with more specific args
  experiment_klasses = base_klass.descendants.reject { |k| k == klass }
  experiment_klasses.push(base_klass).each do |k|
    allow(k).to receive(:new).and_call_original
  end

  receiver = receive(:new)

  # Be specific for BaseClass calls
  receiver = receiver.with(experiment_name, any_args) if experiment_name && klass == base_klass

  receiver.exactly(times).times if times

  # Set expectations on experiment class of interest
  allow_or_expect_klass = expected ? expect(klass) : allow(klass)
  allow_or_expect_klass.to receiver.and_wrap_original do |method, *original_args, &original_block|
    method.call(*original_args).tap do |e|
      # Stub internal methods before calling the original_block
      allow(e).to receive(:enabled?).and_return(true)

      if variant_name == true # passing true allows the rollout to do its job
        allow(e).to receive(:experiment_group?).and_return(true)
      else
        allow(e).to receive(:resolve_variant_name).and_return(variant_name.to_s)
      end

      # Stub/set expectations before calling the original_block
      yield e if block

      original_block.call(e) if original_block.present?
    end
  end
end

Private Instance Methods

experiment_details(experiment) click to toggle source
# File lib/gitlab/experiment/rspec.rb, line 50
def experiment_details(experiment)
  if experiment.is_a?(Symbol)
    experiment_name = experiment
    variant_name = nil
  end

  experiment_name, variant_name = *experiment if experiment.is_a?(Array)

  base_klass = Configuration.base_class.constantize
  variant_name = experiment.variant.name if experiment.is_a?(base_klass)

  if experiment.class.name.nil? # Anonymous class instance
    klass = experiment.class
  elsif experiment.instance_of?(Class) # Class level stubbing, eg. "MyExperiment"
    klass = experiment
  else
    experiment_name ||= experiment.instance_variable_get(:@name)
    klass = base_klass.constantize(experiment_name)
  end

  if experiment_name && klass == base_klass
    experiment_name = experiment_name.to_sym

    # For experiment names like: "group/experiment-name"
    experiment_name = experiment_name.to_s if experiment_name.inspect.include?('"')
  end

  [klass, experiment_name, variant_name]
end