split 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +1 -3
- data/CHANGELOG.mdown +25 -6
- data/Gemfile +1 -1
- data/README.mdown +137 -52
- data/lib/split.rb +1 -1
- data/lib/split/alternative.rb +54 -26
- data/lib/split/configuration.rb +94 -30
- data/lib/split/dashboard/public/style.css +10 -2
- data/lib/split/dashboard/views/_experiment.erb +31 -22
- data/lib/split/dashboard/views/_experiment_with_goal_header.erb +14 -0
- data/lib/split/dashboard/views/index.erb +8 -1
- data/lib/split/experiment.rb +166 -146
- data/lib/split/helper.rb +33 -23
- data/lib/split/metric.rb +13 -0
- data/lib/split/persistence/cookie_adapter.rb +10 -2
- data/lib/split/trial.rb +18 -12
- data/lib/split/version.rb +3 -3
- data/spec/alternative_spec.rb +121 -78
- data/spec/configuration_spec.rb +92 -4
- data/spec/dashboard_spec.rb +27 -11
- data/spec/experiment_spec.rb +110 -67
- data/spec/helper_spec.rb +287 -104
- data/spec/persistence/cookie_adapter_spec.rb +8 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/trial_spec.rb +10 -8
- data/split.gemspec +1 -1
- metadata +19 -18
data/spec/helper_spec.rb
CHANGED
@@ -5,6 +5,10 @@ require 'spec_helper'
|
|
5
5
|
describe Split::Helper do
|
6
6
|
include Split::Helper
|
7
7
|
|
8
|
+
let(:experiment) {
|
9
|
+
Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
10
|
+
}
|
11
|
+
|
8
12
|
describe "ab_test" do
|
9
13
|
|
10
14
|
it "should not raise an error when passed strings for alternatives" do
|
@@ -19,13 +23,20 @@ describe Split::Helper do
|
|
19
23
|
lambda { ab_test('xyz', :a, :b, :c) }.should raise_error(ArgumentError)
|
20
24
|
end
|
21
25
|
|
26
|
+
it "should not raise error when passed an array for goals" do
|
27
|
+
lambda { ab_test({'link_color' => ["purchase", "refund"]}, 'blue', 'red') }.should_not raise_error
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not raise error when passed just one goal" do
|
31
|
+
lambda { ab_test({'link_color' => "purchase"}, 'blue', 'red') }.should_not raise_error
|
32
|
+
end
|
33
|
+
|
22
34
|
it "should assign a random alternative to a new user when there are an equal number of alternatives assigned" do
|
23
35
|
ab_test('link_color', 'blue', 'red')
|
24
36
|
['red', 'blue'].should include(ab_user['link_color'])
|
25
37
|
end
|
26
38
|
|
27
39
|
it "should increment the participation counter after assignment to a new user" do
|
28
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
29
40
|
|
30
41
|
previous_red_count = Split::Alternative.new('red', 'link_color').participant_count
|
31
42
|
previous_blue_count = Split::Alternative.new('blue', 'link_color').participant_count
|
@@ -38,22 +49,37 @@ describe Split::Helper do
|
|
38
49
|
(new_red_count + new_blue_count).should eql(previous_red_count + previous_blue_count + 1)
|
39
50
|
end
|
40
51
|
|
52
|
+
it 'should not increment the counter for an experiment that the user is not participating in' do
|
53
|
+
ab_test('link_color', 'blue', 'red')
|
54
|
+
e = Split::Experiment.find_or_create('button_size', 'small', 'big')
|
55
|
+
lambda {
|
56
|
+
# User shouldn't participate in this second experiment
|
57
|
+
ab_test('button_size', 'small', 'big')
|
58
|
+
}.should_not change { e.participant_count }
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should not increment the counter for an ended experiment' do
|
62
|
+
e = Split::Experiment.find_or_create('button_size', 'small', 'big')
|
63
|
+
e.winner = 'small'
|
64
|
+
lambda {
|
65
|
+
a = ab_test('button_size', 'small', 'big')
|
66
|
+
a.should eq('small')
|
67
|
+
}.should_not change { e.participant_count }
|
68
|
+
end
|
69
|
+
|
41
70
|
it "should return the given alternative for an existing user" do
|
42
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
43
71
|
alternative = ab_test('link_color', 'blue', 'red')
|
44
72
|
repeat_alternative = ab_test('link_color', 'blue', 'red')
|
45
73
|
alternative.should eql repeat_alternative
|
46
74
|
end
|
47
75
|
|
48
76
|
it 'should always return the winner if one is present' do
|
49
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
50
77
|
experiment.winner = "orange"
|
51
78
|
|
52
79
|
ab_test('link_color', 'blue', 'red').should == 'orange'
|
53
80
|
end
|
54
81
|
|
55
82
|
it "should allow the alternative to be force by passing it in the params" do
|
56
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
57
83
|
@params = {'link_color' => 'blue'}
|
58
84
|
alternative = ab_test('link_color', 'blue', 'red')
|
59
85
|
alternative.should eql('blue')
|
@@ -80,7 +106,7 @@ describe Split::Helper do
|
|
80
106
|
it "should allow alternative weighting interface as a single hash" do
|
81
107
|
ab_test('link_color', {'blue' => 0.01}, 'red' => 0.2)
|
82
108
|
experiment = Split::Experiment.find('link_color')
|
83
|
-
experiment.
|
109
|
+
experiment.alternatives.map(&:name).should eql(['blue', 'red'])
|
84
110
|
# TODO: persist alternative weights
|
85
111
|
# experiment.alternatives.collect{|a| a.weight}.should == [0.01, 0.2]
|
86
112
|
end
|
@@ -107,7 +133,6 @@ describe Split::Helper do
|
|
107
133
|
end
|
108
134
|
|
109
135
|
it "should not over-write a finished key when an experiment is on a later version" do
|
110
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
111
136
|
experiment.increment_version
|
112
137
|
ab_user = { experiment.key => 'blue', experiment.finished_key => true }
|
113
138
|
finshed_session = ab_user.dup
|
@@ -132,16 +157,38 @@ describe Split::Helper do
|
|
132
157
|
end
|
133
158
|
|
134
159
|
it "should set experiment's finished key if reset is false" do
|
135
|
-
finished(@experiment_name, :reset => false)
|
160
|
+
finished(@experiment_name, {:reset => false})
|
136
161
|
ab_user.should eql(@experiment.key => @alternative_name, @experiment.finished_key => true)
|
137
162
|
end
|
138
163
|
|
139
164
|
it 'should not increment the counter if reset is false and the experiment has been already finished' do
|
140
|
-
2.times { finished(@experiment_name, :reset => false) }
|
165
|
+
2.times { finished(@experiment_name, {:reset => false}) }
|
141
166
|
new_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count
|
142
167
|
new_completion_count.should eql(@previous_completion_count + 1)
|
143
168
|
end
|
144
169
|
|
170
|
+
it 'should not increment the counter for an experiment that the user is not participating in' do
|
171
|
+
ab_test('button_size', 'small', 'big')
|
172
|
+
|
173
|
+
# So, user should be participating in the link_color experiment and
|
174
|
+
# receive the control for button_size. As the user is not participating in
|
175
|
+
# the button size experiment, finishing it should not increase the
|
176
|
+
# completion count for that alternative.
|
177
|
+
lambda {
|
178
|
+
finished('button_size')
|
179
|
+
}.should_not change { Split::Alternative.new('small', 'button_size').completed_count }
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'should not increment the counter for an ended experiment' do
|
183
|
+
e = Split::Experiment.find_or_create('button_size', 'small', 'big')
|
184
|
+
e.winner = 'small'
|
185
|
+
a = ab_test('button_size', 'small', 'big')
|
186
|
+
a.should eq('small')
|
187
|
+
lambda {
|
188
|
+
finished('button_size')
|
189
|
+
}.should_not change { Split::Alternative.new(a, 'button_size').completed_count }
|
190
|
+
end
|
191
|
+
|
145
192
|
it "should clear out the user's participation from their session" do
|
146
193
|
ab_user.should eql(@experiment.key => @alternative_name)
|
147
194
|
finished(@experiment_name)
|
@@ -150,7 +197,7 @@ describe Split::Helper do
|
|
150
197
|
|
151
198
|
it "should not clear out the users session if reset is false" do
|
152
199
|
ab_user.should eql(@experiment.key => @alternative_name)
|
153
|
-
finished(@experiment_name, :reset => false)
|
200
|
+
finished(@experiment_name, {:reset => false})
|
154
201
|
ab_user.should eql(@experiment.key => @alternative_name, @experiment.finished_key => true)
|
155
202
|
end
|
156
203
|
|
@@ -180,89 +227,107 @@ describe Split::Helper do
|
|
180
227
|
doing_other_tests?(@experiment.key).should be false
|
181
228
|
end
|
182
229
|
|
183
|
-
|
230
|
+
end
|
231
|
+
|
232
|
+
context "finished with config" do
|
233
|
+
it "passes reset option" do
|
184
234
|
Split.configuration.experiments = {
|
185
|
-
|
186
|
-
:alternatives =>
|
235
|
+
:my_experiment => {
|
236
|
+
:alternatives => ["one", "two"],
|
187
237
|
:resettable => false,
|
188
238
|
}
|
189
239
|
}
|
190
|
-
|
191
|
-
|
240
|
+
alternative = ab_test(:my_experiment)
|
241
|
+
experiment = Split::Experiment.find :my_experiment
|
242
|
+
|
243
|
+
finished :my_experiment
|
244
|
+
ab_user.should eql(experiment.key => alternative, experiment.finished_key => true)
|
192
245
|
end
|
246
|
+
end
|
193
247
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
end
|
248
|
+
context "finished with metric name" do
|
249
|
+
before { Split.configuration.experiments = {} }
|
250
|
+
before { Split::Alternative.stub(:new).and_call_original }
|
251
|
+
|
252
|
+
def should_finish_experiment(experiment_name, should_finish=true)
|
253
|
+
alts = Split.configuration.experiments[experiment_name][:alternatives]
|
254
|
+
experiment = Split::Experiment.find_or_create(experiment_name, *alts)
|
255
|
+
alt_name = ab_user[experiment.key] = alts.first
|
256
|
+
alt = mock('alternative')
|
257
|
+
alt.stub(:name).and_return(alt_name)
|
258
|
+
Split::Alternative.stub(:new).with(alt_name, experiment_name.to_s).and_return(alt)
|
259
|
+
if should_finish
|
260
|
+
alt.should_receive(:increment_completion)
|
261
|
+
else
|
262
|
+
alt.should_not_receive(:increment_completion)
|
210
263
|
end
|
264
|
+
end
|
211
265
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
266
|
+
it "completes the test" do
|
267
|
+
Split.configuration.experiments[:my_experiment] = {
|
268
|
+
:alternatives => [ "control_opt", "other_opt" ],
|
269
|
+
:metric => :my_metric
|
270
|
+
}
|
271
|
+
should_finish_experiment :my_experiment
|
272
|
+
finished :my_metric
|
273
|
+
end
|
220
274
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
275
|
+
it "completes all relevant tests" do
|
276
|
+
Split.configuration.experiments = {
|
277
|
+
:exp_1 => {
|
278
|
+
:alternatives => [ "1-1", "1-2" ],
|
279
|
+
:metric => :my_metric
|
280
|
+
},
|
281
|
+
:exp_2 => {
|
282
|
+
:alternatives => [ "2-1", "2-2" ],
|
283
|
+
:metric => :another_metric
|
284
|
+
},
|
285
|
+
:exp_3 => {
|
286
|
+
:alternatives => [ "3-1", "3-2" ],
|
287
|
+
:metric => :my_metric
|
288
|
+
},
|
289
|
+
}
|
290
|
+
should_finish_experiment :exp_1
|
291
|
+
should_finish_experiment :exp_2, false
|
292
|
+
should_finish_experiment :exp_3
|
293
|
+
finished :my_metric
|
294
|
+
end
|
241
295
|
|
242
|
-
|
243
|
-
|
244
|
-
|
296
|
+
it "passes reset option" do
|
297
|
+
Split.configuration.experiments = {
|
298
|
+
:my_exp => {
|
299
|
+
:alternatives => ["one", "two"],
|
245
300
|
:metric => :my_metric,
|
246
301
|
:resettable => false,
|
247
302
|
}
|
248
|
-
|
249
|
-
|
250
|
-
|
303
|
+
}
|
304
|
+
alternative_name = ab_test(:my_exp)
|
305
|
+
exp = Split::Experiment.find :my_exp
|
306
|
+
|
307
|
+
finished :my_metric
|
308
|
+
ab_user[exp.key].should == alternative_name
|
309
|
+
ab_user[exp.finished_key].should == true
|
310
|
+
end
|
251
311
|
|
252
|
-
|
253
|
-
|
254
|
-
|
312
|
+
it "passes through options" do
|
313
|
+
Split.configuration.experiments = {
|
314
|
+
:my_exp => {
|
315
|
+
:alternatives => ["one", "two"],
|
255
316
|
:metric => :my_metric,
|
256
317
|
}
|
257
|
-
|
258
|
-
|
259
|
-
|
318
|
+
}
|
319
|
+
alternative_name = ab_test(:my_exp)
|
320
|
+
exp = Split::Experiment.find :my_exp
|
321
|
+
|
322
|
+
finished :my_metric, :reset => false
|
323
|
+
ab_user[exp.key].should == alternative_name
|
324
|
+
ab_user[exp.finished_key].should == true
|
260
325
|
end
|
326
|
+
|
261
327
|
end
|
262
328
|
|
263
329
|
describe 'conversions' do
|
264
330
|
it 'should return a conversion rate for an alternative' do
|
265
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
266
331
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
267
332
|
|
268
333
|
previous_convertion_rate = Split::Alternative.new(alternative_name, 'link_color').conversion_rate
|
@@ -282,13 +347,11 @@ describe Split::Helper do
|
|
282
347
|
|
283
348
|
describe 'ab_test' do
|
284
349
|
it 'should return the control' do
|
285
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
286
350
|
alternative = ab_test('link_color', 'blue', 'red')
|
287
351
|
alternative.should eql experiment.control.name
|
288
352
|
end
|
289
353
|
|
290
354
|
it "should not increment the participation count" do
|
291
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
292
355
|
|
293
356
|
previous_red_count = Split::Alternative.new('red', 'link_color').participant_count
|
294
357
|
previous_blue_count = Split::Alternative.new('blue', 'link_color').participant_count
|
@@ -304,7 +367,6 @@ describe Split::Helper do
|
|
304
367
|
|
305
368
|
describe 'finished' do
|
306
369
|
it "should not increment the completed count" do
|
307
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
308
370
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
309
371
|
|
310
372
|
previous_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count
|
@@ -318,23 +380,35 @@ describe Split::Helper do
|
|
318
380
|
end
|
319
381
|
end
|
320
382
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
383
|
+
|
384
|
+
describe 'when providing custom ignore logic' do
|
385
|
+
context "using a proc to configure custom logic" do
|
386
|
+
|
387
|
+
before(:each) do
|
388
|
+
Split.configure do |c|
|
389
|
+
c.ignore_filter = proc{|request| !!"i_am_going_to_be_disabled" }
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
it "ignores the ab_test" do
|
394
|
+
ab_test('link_color', 'blue', 'red')
|
395
|
+
|
396
|
+
red_count = Split::Alternative.new('red', 'link_color').participant_count
|
397
|
+
blue_count = Split::Alternative.new('blue', 'link_color').participant_count
|
398
|
+
(red_count + blue_count).should be(0)
|
326
399
|
end
|
327
400
|
end
|
401
|
+
end
|
402
|
+
|
403
|
+
shared_examples_for "a disabled test" do
|
328
404
|
|
329
405
|
describe 'ab_test' do
|
330
406
|
it 'should return the control' do
|
331
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
332
407
|
alternative = ab_test('link_color', 'blue', 'red')
|
333
408
|
alternative.should eql experiment.control.name
|
334
409
|
end
|
335
410
|
|
336
411
|
it "should not increment the participation count" do
|
337
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
338
412
|
|
339
413
|
previous_red_count = Split::Alternative.new('red', 'link_color').participant_count
|
340
414
|
previous_blue_count = Split::Alternative.new('blue', 'link_color').participant_count
|
@@ -350,7 +424,6 @@ describe Split::Helper do
|
|
350
424
|
|
351
425
|
describe 'finished' do
|
352
426
|
it "should not increment the completed count" do
|
353
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
354
427
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
355
428
|
|
356
429
|
previous_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count
|
@@ -364,16 +437,58 @@ describe Split::Helper do
|
|
364
437
|
end
|
365
438
|
end
|
366
439
|
|
440
|
+
describe 'when ip address is ignored' do
|
441
|
+
|
442
|
+
context "individually" do
|
443
|
+
|
444
|
+
before(:each) do
|
445
|
+
@request = OpenStruct.new(:ip => '81.19.48.130')
|
446
|
+
Split.configure do |c|
|
447
|
+
c.ignore_ip_addresses << '81.19.48.130'
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
it_behaves_like "a disabled test"
|
452
|
+
|
453
|
+
end
|
454
|
+
|
455
|
+
context "for a range" do
|
456
|
+
|
457
|
+
before(:each) do
|
458
|
+
@request = OpenStruct.new(:ip => '81.19.48.129')
|
459
|
+
Split.configure do |c|
|
460
|
+
c.ignore_ip_addresses << /81\.19\.48\.[0-9]+/
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
it_behaves_like "a disabled test"
|
465
|
+
|
466
|
+
end
|
467
|
+
|
468
|
+
context "using both a range and a specific value" do
|
469
|
+
|
470
|
+
before(:each) do
|
471
|
+
@request = OpenStruct.new(:ip => '81.19.48.128')
|
472
|
+
Split.configure do |c|
|
473
|
+
c.ignore_ip_addresses << '81.19.48.130'
|
474
|
+
c.ignore_ip_addresses << /81\.19\.48\.[0-9]+/
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
it_behaves_like "a disabled test"
|
479
|
+
|
480
|
+
end
|
481
|
+
|
482
|
+
end
|
483
|
+
|
367
484
|
describe 'versioned experiments' do
|
368
485
|
it "should use version zero if no version is present" do
|
369
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
370
486
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
371
487
|
experiment.version.should eql(0)
|
372
488
|
ab_user.should eql({'link_color' => alternative_name})
|
373
489
|
end
|
374
490
|
|
375
491
|
it "should save the version of the experiment to the session" do
|
376
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
377
492
|
experiment.reset
|
378
493
|
experiment.version.should eql(1)
|
379
494
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
@@ -381,7 +496,6 @@ describe Split::Helper do
|
|
381
496
|
end
|
382
497
|
|
383
498
|
it "should load the experiment even if the version is not 0" do
|
384
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
385
499
|
experiment.reset
|
386
500
|
experiment.version.should eql(1)
|
387
501
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
@@ -391,7 +505,6 @@ describe Split::Helper do
|
|
391
505
|
end
|
392
506
|
|
393
507
|
it "should reset the session of a user on an older version of the experiment" do
|
394
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
395
508
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
396
509
|
ab_user.should eql({'link_color' => alternative_name})
|
397
510
|
alternative = Split::Alternative.new(alternative_name, 'link_color')
|
@@ -409,7 +522,6 @@ describe Split::Helper do
|
|
409
522
|
end
|
410
523
|
|
411
524
|
it "should cleanup old versions of experiments from the session" do
|
412
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
413
525
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
414
526
|
ab_user.should eql({'link_color' => alternative_name})
|
415
527
|
alternative = Split::Alternative.new(alternative_name, 'link_color')
|
@@ -425,7 +537,6 @@ describe Split::Helper do
|
|
425
537
|
end
|
426
538
|
|
427
539
|
it "should only count completion of users on the current version" do
|
428
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
429
540
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
430
541
|
ab_user.should eql({'link_color' => alternative_name})
|
431
542
|
alternative = Split::Alternative.new(alternative_name, 'link_color')
|
@@ -554,6 +665,18 @@ describe Split::Helper do
|
|
554
665
|
end
|
555
666
|
end
|
556
667
|
end
|
668
|
+
|
669
|
+
context 'and preloaded config given' do
|
670
|
+
before do
|
671
|
+
Split.configuration.experiments[:link_color] = {
|
672
|
+
:alternatives => [ "blue", "red" ],
|
673
|
+
}
|
674
|
+
end
|
675
|
+
|
676
|
+
it "uses first alternative" do
|
677
|
+
ab_test(:link_color).should eq("blue")
|
678
|
+
end
|
679
|
+
end
|
557
680
|
end
|
558
681
|
|
559
682
|
describe 'finished' do
|
@@ -577,34 +700,56 @@ describe Split::Helper do
|
|
577
700
|
|
578
701
|
context "with preloaded config" do
|
579
702
|
before { Split.configuration.experiments = {}}
|
580
|
-
|
703
|
+
|
581
704
|
it "pulls options from config file" do
|
582
705
|
Split.configuration.experiments[:my_experiment] = {
|
583
706
|
:alternatives => [ "control_opt", "other_opt" ],
|
707
|
+
:goals => ["goal1", "goal2"]
|
584
708
|
}
|
585
709
|
ab_test :my_experiment
|
586
|
-
Split::Experiment.
|
710
|
+
Split::Experiment.new(:my_experiment).alternatives.map(&:name).should == [ "control_opt", "other_opt" ]
|
711
|
+
Split::Experiment.new(:my_experiment).goals.should == [ "goal1", "goal2" ]
|
587
712
|
end
|
588
|
-
|
713
|
+
|
589
714
|
it "can be called multiple times" do
|
590
715
|
Split.configuration.experiments[:my_experiment] = {
|
591
716
|
:alternatives => [ "control_opt", "other_opt" ],
|
717
|
+
:goals => ["goal1", "goal2"]
|
592
718
|
}
|
593
719
|
5.times { ab_test :my_experiment }
|
594
|
-
experiment = Split::Experiment.
|
595
|
-
experiment.
|
720
|
+
experiment = Split::Experiment.new(:my_experiment)
|
721
|
+
experiment.alternatives.map(&:name).should == [ "control_opt", "other_opt" ]
|
722
|
+
experiment.goals.should == [ "goal1", "goal2" ]
|
596
723
|
experiment.participant_count.should == 1
|
597
724
|
end
|
598
|
-
|
725
|
+
|
726
|
+
it "accepts multiple goals" do
|
727
|
+
Split.configuration.experiments[:my_experiment] = {
|
728
|
+
:alternatives => [ "control_opt", "other_opt" ],
|
729
|
+
:goals => [ "goal1", "goal2", "goal3" ]
|
730
|
+
}
|
731
|
+
ab_test :my_experiment
|
732
|
+
experiment = Split::Experiment.new(:my_experiment)
|
733
|
+
experiment.goals.should == [ "goal1", "goal2", "goal3" ]
|
734
|
+
end
|
735
|
+
|
736
|
+
it "allow specifying goals to be optional" do
|
737
|
+
Split.configuration.experiments[:my_experiment] = {
|
738
|
+
:alternatives => [ "control_opt", "other_opt" ]
|
739
|
+
}
|
740
|
+
experiment = Split::Experiment.new(:my_experiment)
|
741
|
+
experiment.goals.should == []
|
742
|
+
end
|
743
|
+
|
599
744
|
it "accepts multiple alternatives" do
|
600
745
|
Split.configuration.experiments[:my_experiment] = {
|
601
746
|
:alternatives => [ "control_opt", "second_opt", "third_opt" ],
|
602
747
|
}
|
603
748
|
ab_test :my_experiment
|
604
|
-
experiment = Split::Experiment.
|
605
|
-
experiment.
|
749
|
+
experiment = Split::Experiment.new(:my_experiment)
|
750
|
+
experiment.alternatives.map(&:name).should == [ "control_opt", "second_opt", "third_opt" ]
|
606
751
|
end
|
607
|
-
|
752
|
+
|
608
753
|
it "accepts probability on alternatives" do
|
609
754
|
Split.configuration.experiments[:my_experiment] = {
|
610
755
|
:alternatives => [
|
@@ -614,11 +759,11 @@ describe Split::Helper do
|
|
614
759
|
],
|
615
760
|
}
|
616
761
|
ab_test :my_experiment
|
617
|
-
experiment = Split::Experiment.
|
762
|
+
experiment = Split::Experiment.new(:my_experiment)
|
618
763
|
experiment.alternatives.collect{|a| [a.name, a.weight]}.should == [['control_opt', 0.67], ['second_opt', 0.1], ['third_opt', 0.23]]
|
619
|
-
|
764
|
+
|
620
765
|
end
|
621
|
-
|
766
|
+
|
622
767
|
it "accepts probability on some alternatives" do
|
623
768
|
Split.configuration.experiments[:my_experiment] = {
|
624
769
|
:alternatives => [
|
@@ -629,12 +774,12 @@ describe Split::Helper do
|
|
629
774
|
],
|
630
775
|
}
|
631
776
|
ab_test :my_experiment
|
632
|
-
experiment = Split::Experiment.
|
777
|
+
experiment = Split::Experiment.new(:my_experiment)
|
633
778
|
names_and_weights = experiment.alternatives.collect{|a| [a.name, a.weight]}
|
634
779
|
names_and_weights.should == [['control_opt', 0.34], ['second_opt', 0.215], ['third_opt', 0.23], ['fourth_opt', 0.215]]
|
635
780
|
names_and_weights.inject(0){|sum, nw| sum + nw[1]}.should == 1.0
|
636
781
|
end
|
637
|
-
|
782
|
+
|
638
783
|
it "allows name param without probability" do
|
639
784
|
Split.configuration.experiments[:my_experiment] = {
|
640
785
|
:alternatives => [
|
@@ -644,7 +789,7 @@ describe Split::Helper do
|
|
644
789
|
],
|
645
790
|
}
|
646
791
|
ab_test :my_experiment
|
647
|
-
experiment = Split::Experiment.
|
792
|
+
experiment = Split::Experiment.new(:my_experiment)
|
648
793
|
names_and_weights = experiment.alternatives.collect{|a| [a.name, a.weight]}
|
649
794
|
names_and_weights.should == [['control_opt', 0.18], ['second_opt', 0.18], ['third_opt', 0.64]]
|
650
795
|
names_and_weights.inject(0){|sum, nw| sum + nw[1]}.should == 1.0
|
@@ -667,7 +812,6 @@ describe Split::Helper do
|
|
667
812
|
end
|
668
813
|
|
669
814
|
it 'should handle multiple experiments correctly' do
|
670
|
-
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
671
815
|
experiment2 = Split::Experiment.find_or_create('link_color2', 'blue', 'red')
|
672
816
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
673
817
|
alternative_name2 = ab_test('link_color2', 'blue', 'red')
|
@@ -677,4 +821,43 @@ describe Split::Helper do
|
|
677
821
|
alt.unfinished_count.should eq(0)
|
678
822
|
end
|
679
823
|
end
|
824
|
+
|
825
|
+
context "with goals" do
|
826
|
+
before do
|
827
|
+
@experiment = {'link_color' => ["purchase", "refund"]}
|
828
|
+
@alternatives = ['blue', 'red']
|
829
|
+
@experiment_name, @goals = normalize_experiment(@experiment)
|
830
|
+
@goal1 = @goals[0]
|
831
|
+
@goal2 = @goals[1]
|
832
|
+
end
|
833
|
+
|
834
|
+
it "should normalize experiment" do
|
835
|
+
@experiment_name.should eql("link_color")
|
836
|
+
@goals.should eql(["purchase", "refund"])
|
837
|
+
end
|
838
|
+
|
839
|
+
describe "ab_test" do
|
840
|
+
it "should allow experiment goals interface as a single hash" do
|
841
|
+
ab_test(@experiment, *@alternatives)
|
842
|
+
experiment = Split::Experiment.find('link_color')
|
843
|
+
experiment.goals.should eql(['purchase', "refund"])
|
844
|
+
end
|
845
|
+
end
|
846
|
+
|
847
|
+
describe "finished" do
|
848
|
+
before do
|
849
|
+
@alternative_name = ab_test(@experiment, *@alternatives)
|
850
|
+
end
|
851
|
+
|
852
|
+
it "should increment the counter for the specified-goal completed alternative" do
|
853
|
+
@previous_completion_count_for_goal1 = Split::Alternative.new(@alternative_name, @experiment_name).completed_count(@goal1)
|
854
|
+
@previous_completion_count_for_goal2 = Split::Alternative.new(@alternative_name, @experiment_name).completed_count(@goal2)
|
855
|
+
finished({"link_color" => "purchase"})
|
856
|
+
new_completion_count_for_goal1 = Split::Alternative.new(@alternative_name, @experiment_name).completed_count(@goal1)
|
857
|
+
new_completion_count_for_goal1.should eql(@previous_completion_count_for_goal1 + 1)
|
858
|
+
new_completion_count_for_goal2 = Split::Alternative.new(@alternative_name, @experiment_name).completed_count(@goal2)
|
859
|
+
new_completion_count_for_goal2.should eql(@previous_completion_count_for_goal2)
|
860
|
+
end
|
861
|
+
end
|
862
|
+
end
|
680
863
|
end
|