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.
@@ -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.alternative_names.should eql(['blue', 'red'])
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
- it "passes reset option from config" do
230
+ end
231
+
232
+ context "finished with config" do
233
+ it "passes reset option" do
184
234
  Split.configuration.experiments = {
185
- @experiment_name => {
186
- :alternatives => @alternatives,
235
+ :my_experiment => {
236
+ :alternatives => ["one", "two"],
187
237
  :resettable => false,
188
238
  }
189
239
  }
190
- finished @experiment_name
191
- ab_user.should eql(@experiment.key => @alternative_name, @experiment.finished_key => true)
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
- context "with metric name" do
195
- before { Split.configuration.experiments = {} }
196
- before { Split::Alternative.stub(:new).and_call_original }
197
-
198
- def should_finish_experiment(experiment_name, should_finish=true)
199
- alts = Split.configuration.experiments[experiment_name][:alternatives]
200
- experiment = Split::Experiment.find_or_create(experiment_name, *alts)
201
- alt_name = ab_user[experiment.key] = alts.first
202
- alt = mock('alternative')
203
- alt.stub(:name).and_return(alt_name)
204
- Split::Alternative.stub(:new).with(alt_name, experiment_name).and_return(alt)
205
- if should_finish
206
- alt.should_receive(:increment_completion)
207
- else
208
- alt.should_not_receive(:increment_completion)
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
- it "completes the test" do
213
- Split.configuration.experiments[:my_experiment] = {
214
- :alternatives => [ "control_opt", "other_opt" ],
215
- :metric => :my_metric
216
- }
217
- should_finish_experiment :my_experiment
218
- finished :my_metric
219
- end
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
- it "completes all relevant tests" do
222
- Split.configuration.experiments = {
223
- :exp_1 => {
224
- :alternatives => [ "1-1", "1-2" ],
225
- :metric => :my_metric
226
- },
227
- :exp_2 => {
228
- :alternatives => [ "2-1", "2-2" ],
229
- :metric => :another_metric
230
- },
231
- :exp_3 => {
232
- :alternatives => [ "3-1", "3-2" ],
233
- :metric => :my_metric
234
- },
235
- }
236
- should_finish_experiment :exp_1
237
- should_finish_experiment :exp_2, false
238
- should_finish_experiment :exp_3
239
- finished :my_metric
240
- end
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
- it "passes reset option" do
243
- Split.configuration.experiments[@experiment_name] = {
244
- :alternatives => @alternatives,
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
- finished :my_metric
249
- ab_user.should eql(@experiment.key => @alternative_name, @experiment.finished_key => true)
250
- end
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
- it "passes through options" do
253
- Split.configuration.experiments[@experiment_name] = {
254
- :alternatives => @alternatives,
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
- finished :my_metric, :reset => false
258
- ab_user.should eql(@experiment.key => @alternative_name, @experiment.finished_key => true)
259
- end
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
- describe 'when ip address is ignored' do
322
- before(:each) do
323
- @request = OpenStruct.new(:ip => '81.19.48.130')
324
- Split.configure do |c|
325
- c.ignore_ip_addresses << '81.19.48.130'
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.find(:my_experiment).alternative_names.should == [ "control_opt", "other_opt" ]
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.find(:my_experiment)
595
- experiment.alternative_names.should == [ "control_opt", "other_opt" ]
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.find(:my_experiment)
605
- experiment.alternative_names.should == [ "control_opt", "second_opt", "third_opt" ]
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.find(:my_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.find(:my_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.find(:my_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