실험 테스트
GitLab v19.4요약
실험을 다루다 보면 내장된 RSpec 도구를 사용하고 싶을 수 있습니다. :experiment 타입 없이도 사용할 수 있는 stub_experiments로 실험을 스텁 처리할 수 있습니다. 매처를 사용하면 등록된 동작, 제외, 세그멘테이션 같은 항목도 테스트할 수 있습니다.
RSpec으로 실험 테스트#
실험을 다루다 보면 내장된 RSpec 도구를 사용하고 싶을 수 있습니다.
spec/experiments의 파일에서는 자동으로 적용되지만,
그 밖의 파일과 스펙에 포함하려면 :experiment 타입을 지정합니다.
it "tests experiments nicely", :experiment do
end
Stub 헬퍼#
:experiment 타입 없이도 사용할 수 있는 stub_experiments로 실험을 스텁 처리할 수 있습니다.
실험 이름을 키로, 각 실험이 해석될 배리언트를 값으로 하는 해시를 전달합니다.
# Ensures the experiments named `:example` & `:example2` are both "enabled" and
# that each will resolve to the given variant (`:my_variant` and `:control`).
stub_experiments(example: :my_variant, example2: :control)
experiment(:example) do |e|
e.enabled? # => true
e.assigned.name # => 'my_variant'
end
experiment(:example2) do |e|
e.enabled? # => true
e.assigned.name # => 'control'
end
제외, 세그멘테이션 및 동작 매처#
매처를 사용하면 등록된 동작, 제외, 세그멘테이션 같은 항목도 테스트할 수 있습니다.
class ExampleExperiment < ApplicationExperiment
control { }
candidate { '_candidate_' }
exclude { context.actor.first_name == 'Richard' }
segment(variant: :candidate) { context.actor.username == 'jejacks0n' }
end
excluded = double(username: 'rdiggitty', first_name: 'Richard')
segmented = double(username: 'jejacks0n', first_name: 'Jeremy')
# register_behavior matcher
expect(experiment(:example)).to register_behavior(:control)
expect(experiment(:example)).to register_behavior(:candidate).with('_candidate_')
# exclude matcher
expect(experiment(:example)).to exclude(actor: excluded)
expect(experiment(:example)).not_to exclude(actor: segmented)
# segment matcher
expect(experiment(:example)).to segment(actor: segmented).into(:candidate)
expect(experiment(:example)).not_to segment(actor: excluded)
트래킹 매처#
이벤트 트래킹은 실험에서 중요한 축입니다. 트래킹 호출을 유연하게 검증할 수 있는 방법을 제공합니다.
인스턴스 수준에서도, "임의의 인스턴스" 수준에서도 검증할 수 있습니다.
subject = experiment(:example)
expect(subject).to track(:my_event)
subject.track(:my_event)
on_next_instance 체인 메서드를 사용하면 해당 실험의 다음 인스턴스에서
트래킹이 일어난다고 지정할 수 있습니다. 하위 흐름에서
experiment(:example).track을 호출할 때 유용합니다.
expect(experiment(:example)).to track(:my_event).on_next_instance
experiment(:example).track(:my_event)
track 매처에 체인할 수 있는 메서드의 전체 예시는 다음과 같습니다.
expect(experiment(:example)).to track(:my_event, value: 1, property: '_property_')
.on_next_instance
.with_context(foo: :bar)
.for(:variant_name)
experiment(:example, :variant_name, foo: :bar).track(:my_event, value: 1, property: '_property_')
실험이 발생시키는 이벤트 검증#
track 매처는 실험에 무언가를 트래킹하도록 요청했는지를 검증합니다.
Snowplow에 도달하는 페이로드는 확인하지 않으며, 브라우저가 보내는 이벤트는 전혀
확인하지 못합니다.
두 가지를 모두 검증하려면 기능 스펙에 :capture_snowplow_events 태그를 붙이고 각
배리언트가 발생시켜야 하는 이벤트를 트래킹 여정 계약에 선언합니다.
계약은 실험 이름을 한 번만 지정하므로, 테스트 대상 분기의 모든 이벤트도 해당 실험의
컨텍스트를 함께 담아야 합니다.
expect_snowplow_tracking_journey('whats_new_placement', variant: variant)
계약 형식과 나머지 동작 방식은 기능 스펙에서 Snowplow 이벤트 캡처를 참고합니다.
Jest로 테스트#
Stub 헬퍼#
spec/frontend/__helpers__/experimentation_helper.js에 정의된 stubExperiments 헬퍼로 실험을 스텁 처리할 수 있습니다.
import { stubExperiments } from 'helpers/experimentation_helper';
import { getExperimentData } from '~/experimentation/utils';
describe('when my_experiment is enabled', () => {
beforeEach(() => {
stubExperiments({ my_experiment: 'candidate' });
});
it('sets the correct data', () => {
expect(getExperimentData('my_experiment')).toEqual({ experiment: 'my_experiment', variant: 'candidate' });
});
});
Jest 스펙에서 이 방식으로 스텁을 적용하면 테스트가 끝날 때 자동으로 해제되지 않습니다. 스텁 처리한 실험은 window.gl의 다른 전역 데이터와 함께 병합됩니다. 테스트 후 스텁 처리한 실험을 제거하거나 테스트 전에 깨끗한 전역 객체를 보장해야 한다면, 전역 객체를 직접 관리해야 합니다.
describe('tests that care about global state', () => {
const originalObjects = [];
beforeEach(() => {
// For backwards compatibility for now, we're using both window.gon & window.gl
originalObjects.push(window.gon, window.gl);
});
afterEach(() => {
[window.gon, window.gl] = originalObjects;
});
it('stubs experiment in fresh global state', () => {
stubExperiment({ my_experiment: 'candidate' });
// ...
});
})