vanity 1.9.3 → 2.0.0.beta

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.
Files changed (54) hide show
  1. data/.gitignore +0 -1
  2. data/.travis.yml +2 -53
  3. data/Appraisals +3 -12
  4. data/CHANGELOG +1 -5
  5. data/Gemfile +0 -10
  6. data/Gemfile.lock +3 -38
  7. data/README.rdoc +2 -13
  8. data/Rakefile +7 -13
  9. data/bin/vanity +0 -1
  10. data/doc/rails.textile +0 -12
  11. data/gemfiles/rails32.gemfile +2 -4
  12. data/gemfiles/rails32.gemfile.lock +6 -14
  13. data/gemfiles/rails4.gemfile +1 -4
  14. data/gemfiles/rails4.gemfile.lock +5 -15
  15. data/lib/vanity/adapters/active_record_adapter.rb +14 -10
  16. data/lib/vanity/experiment/ab_test.rb +1 -1
  17. data/lib/vanity/frameworks/rails.rb +11 -17
  18. data/lib/vanity/frameworks.rb +3 -10
  19. data/lib/vanity/metric/active_record.rb +20 -19
  20. data/lib/vanity/playground.rb +1 -3
  21. data/lib/vanity/version.rb +1 -1
  22. data/test/adapters/redis_adapter_test.rb +26 -27
  23. data/test/autoconnect_test.rb +19 -17
  24. data/test/cli_test.rb +19 -26
  25. data/test/experiment/ab_test.rb +2 -15
  26. data/test/experiment/base_test.rb +18 -22
  27. data/test/frameworks/rails/action_controller_test.rb +184 -0
  28. data/test/frameworks/rails/action_mailer_test.rb +65 -0
  29. data/test/{rails_helper_test.rb → frameworks/rails/action_view_test.rb} +1 -1
  30. data/test/frameworks/rails/rails_test.rb +285 -0
  31. data/test/helper_test.rb +13 -10
  32. data/test/metric/active_record_test.rb +50 -66
  33. data/test/metric/base_test.rb +39 -41
  34. data/test/metric/google_analytics_test.rb +13 -16
  35. data/test/metric/remote_test.rb +18 -19
  36. data/test/playground_test.rb +1 -1
  37. data/test/test_helper.rb +25 -43
  38. data/test/{rails_dashboard_test.rb → web/rails/dashboard_test.rb} +2 -1
  39. data/vanity.gemspec +3 -2
  40. metadata +33 -33
  41. data/gemfiles/rails3.gemfile +0 -32
  42. data/gemfiles/rails3.gemfile.lock +0 -172
  43. data/gemfiles/rails31.gemfile +0 -32
  44. data/gemfiles/rails31.gemfile.lock +0 -181
  45. data/generators/templates/vanity_migration.rb +0 -54
  46. data/generators/vanity_generator.rb +0 -8
  47. data/test/myapp/app/controllers/application_controller.rb +0 -2
  48. data/test/myapp/app/controllers/main_controller.rb +0 -7
  49. data/test/myapp/config/boot.rb +0 -110
  50. data/test/myapp/config/environment.rb +0 -10
  51. data/test/myapp/config/environments/production.rb +0 -0
  52. data/test/myapp/config/routes.rb +0 -3
  53. data/test/passenger_test.rb +0 -52
  54. data/test/rails_test.rb +0 -554
@@ -0,0 +1,184 @@
1
+ require "test_helper"
2
+
3
+ class UseVanityController < ActionController::Base
4
+ attr_accessor :current_user
5
+
6
+ def index
7
+ render :text=>ab_test(:pie_or_cake)
8
+ end
9
+
10
+ def js
11
+ ab_test(:pie_or_cake)
12
+ render :inline => "<%= vanity_js -%>"
13
+ end
14
+ end
15
+
16
+ # Pages accessible to everyone, e.g. sign in, community search.
17
+ class UseVanityControllerTest < ActionController::TestCase
18
+ tests UseVanityController
19
+
20
+ def setup
21
+ super
22
+ metric :sugar_high
23
+ new_ab_test :pie_or_cake do
24
+ metrics :sugar_high
25
+ end
26
+ UseVanityController.class_eval do
27
+ use_vanity :current_user
28
+ end
29
+ if ::Rails.respond_to?(:application) # Rails 3 configuration
30
+ ::Rails.application.config.session_options[:domain] = '.foo.bar'
31
+ end
32
+ end
33
+
34
+ def test_render_js_for_tests
35
+ Vanity.playground.use_js!
36
+ get :js
37
+ assert_match /script.*e=pie_or_cake.*script/m, @response.body
38
+ end
39
+
40
+ def test_chooses_sets_alternatives_for_rails_tests
41
+ experiment(:pie_or_cake).chooses(true)
42
+ get :index
43
+ assert_equal 'true', @response.body
44
+
45
+ experiment(:pie_or_cake).chooses(false)
46
+ get :index
47
+ assert_equal 'false', @response.body
48
+ end
49
+
50
+ def test_adds_participant_to_experiment
51
+ get :index
52
+ assert_equal 1, experiment(:pie_or_cake).alternatives.map(&:participants).sum
53
+ end
54
+
55
+ def test_does_not_add_invalid_participant_to_experiment
56
+ @request.user_agent = "Googlebot/2.1 ( http://www.google.com/bot.html)"
57
+ get :index
58
+ assert_equal 0, experiment(:pie_or_cake).alternatives.map(&:participants).sum
59
+ end
60
+
61
+ def test_vanity_cookie_is_persistent
62
+ get :index
63
+ cookie = @response["Set-Cookie"].to_s
64
+ assert_match /vanity_id=[a-f0-9]{32};/, cookie
65
+ expires = cookie[/expires=(.*)(;|$)/, 1]
66
+ assert expires
67
+ assert_in_delta Time.parse(expires), Time.now + 1.month, 1.day
68
+ end
69
+
70
+ def test_vanity_cookie_default_id
71
+ get :index
72
+ assert cookies["vanity_id"] =~ /^[a-f0-9]{32}$/
73
+ end
74
+
75
+ def test_vanity_cookie_retains_id
76
+ @request.cookies["vanity_id"] = "from_last_time"
77
+ get :index
78
+ assert_equal "from_last_time", cookies["vanity_id"]
79
+ end
80
+
81
+ def test_vanity_identity_set_from_cookie
82
+ @request.cookies["vanity_id"] = "from_last_time"
83
+ get :index
84
+ assert_equal "from_last_time", @controller.send(:vanity_identity)
85
+ end
86
+
87
+ def test_vanity_identity_set_from_user
88
+ @controller.current_user = mock("user", :id=>"user_id")
89
+ get :index
90
+ assert_equal "user_id", @controller.send(:vanity_identity)
91
+ end
92
+
93
+ def test_vanity_identity_with_no_user_model
94
+ UseVanityController.class_eval do
95
+ use_vanity nil
96
+ end
97
+ @controller.current_user = Object.new
98
+ get :index
99
+ assert cookies["vanity_id"] =~ /^[a-f0-9]{32}$/
100
+ end
101
+
102
+ def test_vanity_identity_set_with_block
103
+ UseVanityController.class_eval do
104
+ attr_accessor :project_id
105
+ use_vanity { |controller| controller.project_id }
106
+ end
107
+ @controller.project_id = "576"
108
+ get :index
109
+ assert_equal "576", @controller.send(:vanity_identity)
110
+ end
111
+
112
+ def test_vanity_identity_set_with_indentity_paramater
113
+ get :index, :_identity => "id_from_params"
114
+ assert_equal "id_from_params", @controller.send(:vanity_identity)
115
+ end
116
+
117
+ def test_vanity_identity_prefers_block_over_symbol
118
+ UseVanityController.class_eval do
119
+ attr_accessor :project_id
120
+ use_vanity(:current_user) { |controller| controller.project_id }
121
+ end
122
+ @controller.project_id = "576"
123
+ @controller.current_user = stub(:id=>"user_id")
124
+
125
+ get :index
126
+ assert_equal "576", @controller.send(:vanity_identity)
127
+ end
128
+
129
+ def test_vanity_identity_prefers_parameter_over_cookie
130
+ @request.cookies['vanity_id'] = "old_id"
131
+ get :index, :_identity => "id_from_params"
132
+ assert_equal "id_from_params", @controller.send(:vanity_identity)
133
+ assert cookies['vanity_id'], "id_from_params"
134
+ end
135
+
136
+ def test_vanity_identity_prefers_cookie_over_object
137
+ @request.cookies['vanity_id'] = "from_last_time"
138
+ @controller.current_user = stub(:id=>"user_id")
139
+ get :index
140
+ assert_equal "from_last_time", @controller.send(:vanity_identity)
141
+ end
142
+
143
+ # query parameter filter
144
+
145
+ def test_redirects_and_loses_vanity_query_parameter
146
+ get :index, :foo=>"bar", :_vanity=>"567"
147
+ assert_redirected_to "/use_vanity?foo=bar"
148
+ end
149
+
150
+ def test_sets_choices_from_vanity_query_parameter
151
+ first = experiment(:pie_or_cake).alternatives.first
152
+ fingerprint = experiment(:pie_or_cake).fingerprint(first)
153
+ 10.times do
154
+ @controller = nil ; setup_controller_request_and_response
155
+ get :index, :_vanity => fingerprint
156
+ assert_equal experiment(:pie_or_cake).choose, experiment(:pie_or_cake).alternatives.first
157
+ assert experiment(:pie_or_cake).showing?(first)
158
+ end
159
+ end
160
+
161
+ def test_does_nothing_with_vanity_query_parameter_for_posts
162
+ experiment(:pie_or_cake).chooses(experiment(:pie_or_cake).alternatives.last.value)
163
+ first = experiment(:pie_or_cake).alternatives.first
164
+ fingerprint = experiment(:pie_or_cake).fingerprint(first)
165
+ post :index, :foo => "bar", :_vanity => fingerprint
166
+ assert_response :success
167
+ assert !experiment(:pie_or_cake).showing?(first)
168
+ end
169
+
170
+ def test_track_param_tracks_a_metric
171
+ get :index, :_identity => "123", :_track => "sugar_high"
172
+ assert_equal experiment(:pie_or_cake).alternatives[0].converted, 1
173
+ end
174
+
175
+ def test_cookie_domain_from_rails_configuration
176
+ get :index
177
+ assert_match /domain=.foo.bar/, @response["Set-Cookie"] if ::Rails.respond_to?(:application)
178
+ end
179
+
180
+ def teardown
181
+ super
182
+ end
183
+
184
+ end
@@ -0,0 +1,65 @@
1
+ require "test_helper"
2
+
3
+ class VanityMailer < ActionMailer::Base
4
+ include Vanity::Rails::Helpers
5
+ include ActionView::Helpers::AssetTagHelper
6
+ include ActionView::Helpers::TagHelper
7
+
8
+ def ab_test_subject(user, forced_outcome=true)
9
+ use_vanity_mailer user
10
+ experiment(:pie_or_cake).chooses(forced_outcome)
11
+
12
+ if defined?(Rails::Railtie)
13
+ mail :subject =>ab_test(:pie_or_cake).to_s, :body => ""
14
+ else
15
+ subject ab_test(:pie_or_cake).to_s
16
+ body ""
17
+ end
18
+ end
19
+
20
+ def ab_test_content(user)
21
+ use_vanity_mailer user
22
+
23
+ if defined?(Rails::Railtie)
24
+ mail do |format|
25
+ format.html { render :text=>view_context.vanity_tracking_image(Vanity.context.vanity_identity, :open, :host => "127.0.0.1:3000") }
26
+ end
27
+ else
28
+ body vanity_tracking_image(Vanity.context.vanity_identity, :open, :host => "127.0.0.1:3000")
29
+ end
30
+ end
31
+ end
32
+
33
+ class UseVanityMailerTest < ActionMailer::TestCase
34
+ tests VanityMailer
35
+
36
+ def setup
37
+ super
38
+ metric :sugar_high
39
+ new_ab_test :pie_or_cake do
40
+ metrics :sugar_high
41
+ end
42
+ end
43
+
44
+ def test_js_enabled_still_adds_participant
45
+ Vanity.playground.use_js!
46
+ VanityMailer.ab_test_subject(nil, true)
47
+
48
+ alts = experiment(:pie_or_cake).alternatives
49
+ assert_equal 1, alts.map(&:participants).sum
50
+ end
51
+
52
+ def test_returns_different_alternatives
53
+ email = VanityMailer.ab_test_subject(nil, true)
54
+ assert_equal 'true', email.subject
55
+
56
+ email = VanityMailer.ab_test_subject(nil, false)
57
+ assert_equal 'false', email.subject
58
+ end
59
+
60
+ def test_tracking_image_is_rendered
61
+ email = VanityMailer.ab_test_content(nil)
62
+ assert email.body =~ /<img/
63
+ assert email.body =~ /_identity=/
64
+ end
65
+ end
@@ -1,4 +1,4 @@
1
- require "test/test_helper"
1
+ require "test_helper"
2
2
 
3
3
  class RailsHelperTest < ActionView::TestCase
4
4
  include Vanity::Rails::Helpers
@@ -0,0 +1,285 @@
1
+ require "test_helper"
2
+
3
+ class LoadPathAndConnectionConfigurationTest < Test::Unit::TestCase
4
+
5
+ def test_load_path
6
+ assert_equal File.expand_path("tmp/experiments"), load_rails("", <<-RB)
7
+ $stdout << Vanity.playground.load_path
8
+ RB
9
+ end
10
+
11
+ def test_settable_load_path
12
+ assert_equal File.expand_path("tmp/predictions"), load_rails(%Q{\nVanity.playground.load_path = "predictions"\n}, <<-RB)
13
+ $stdout << Vanity.playground.load_path
14
+ RB
15
+ end
16
+
17
+ def test_absolute_load_path
18
+ Dir.mktmpdir do |dir|
19
+ assert_equal dir, load_rails(%Q{\nVanity.playground.load_path = "#{dir}"\n}, <<-RB)
20
+ $stdout << Vanity.playground.load_path
21
+ RB
22
+ end
23
+ end
24
+
25
+ if ENV['DB'] == 'redis'
26
+ def test_default_connection
27
+ assert_equal "redis://127.0.0.1:6379/0", load_rails("", <<-RB)
28
+ $stdout << Vanity.playground.connection
29
+ RB
30
+ end
31
+
32
+ def test_connection_from_string
33
+ assert_equal "redis://192.168.1.1:6379/5", load_rails(%Q{\nVanity.playground.establish_connection "redis://192.168.1.1:6379/5"\n}, <<-RB)
34
+ $stdout << Vanity.playground.connection
35
+ RB
36
+ end
37
+
38
+ def test_connection_from_yaml
39
+ FileUtils.mkpath "tmp/config"
40
+ @original_env = ENV["RAILS_ENV"]
41
+ ENV["RAILS_ENV"] = "production"
42
+ File.open("tmp/config/vanity.yml", "w") do |io|
43
+ io.write <<-YML
44
+ production:
45
+ adapter: redis
46
+ host: somehost
47
+ database: 15
48
+ YML
49
+ end
50
+ assert_equal "redis://somehost:6379/15", load_rails("", <<-RB)
51
+ $stdout << Vanity.playground.connection
52
+ RB
53
+ ensure
54
+ ENV["RAILS_ENV"] = @original_env
55
+ File.unlink "tmp/config/vanity.yml"
56
+ end
57
+
58
+ def test_connection_from_yaml_url
59
+ FileUtils.mkpath "tmp/config"
60
+ @original_env = ENV["RAILS_ENV"]
61
+ ENV["RAILS_ENV"] = "production"
62
+ File.open("tmp/config/vanity.yml", "w") do |io|
63
+ io.write <<-YML
64
+ production: redis://somehost/15
65
+ YML
66
+ end
67
+ assert_equal "redis://somehost:6379/15", load_rails("", <<-RB)
68
+ $stdout << Vanity.playground.connection
69
+ RB
70
+ ensure
71
+ ENV["RAILS_ENV"] = @original_env
72
+ File.unlink "tmp/config/vanity.yml"
73
+ end
74
+
75
+ def test_connection_from_yaml_with_erb
76
+ FileUtils.mkpath "tmp/config"
77
+ @original_env = ENV["RAILS_ENV"]
78
+ ENV["RAILS_ENV"] = "production"
79
+ # Pass storage URL through environment like heroku does
80
+ @original_redis_url = ENV["REDIS_URL"]
81
+ ENV["REDIS_URL"] = "redis://somehost:6379/15"
82
+ File.open("tmp/config/vanity.yml", "w") do |io|
83
+ io.write <<-YML
84
+ production: <%= ENV['REDIS_URL'] %>
85
+ YML
86
+ end
87
+ assert_equal "redis://somehost:6379/15", load_rails("", <<-RB)
88
+ $stdout << Vanity.playground.connection
89
+ RB
90
+ ensure
91
+ ENV["RAILS_ENV"] = @original_env
92
+ ENV["REDIS_URL"] = @original_redis_url
93
+ File.unlink "tmp/config/vanity.yml"
94
+ end
95
+
96
+ def test_connection_from_redis_yml
97
+ FileUtils.mkpath "tmp/config"
98
+ yml = File.open("tmp/config/redis.yml", "w")
99
+ yml << "production: internal.local:6379\n"
100
+ yml.flush
101
+ assert_equal "redis://internal.local:6379/0", load_rails("", <<-RB)
102
+ $stdout << Vanity.playground.connection
103
+ RB
104
+ ensure
105
+ File.unlink yml.path
106
+ end
107
+ end
108
+
109
+ if ENV['DB'] == 'mongo'
110
+ def test_mongo_connection_from_yaml
111
+ FileUtils.mkpath "tmp/config"
112
+ File.open("tmp/config/vanity.yml", "w") do |io|
113
+ io.write <<-YML
114
+ mongodb:
115
+ adapter: mongodb
116
+ host: localhost
117
+ port: 27017
118
+ database: vanity_test
119
+ YML
120
+ end
121
+
122
+ assert_equal "mongodb://localhost:27017/vanity_test", load_rails("", <<-RB, "mongodb")
123
+ $stdout << Vanity.playground.connection
124
+ RB
125
+ ensure
126
+ File.unlink "tmp/config/vanity.yml"
127
+ end
128
+
129
+ unless ENV['CI'] == 'true' #TODO this doesn't get tested on CI
130
+ def test_mongodb_replica_set_connection
131
+ FileUtils.mkpath "tmp/config"
132
+ File.open("tmp/config/vanity.yml", "w") do |io|
133
+ io.write <<-YML
134
+ mongodb:
135
+ adapter: mongodb
136
+ hosts:
137
+ - localhost
138
+ port: 27017
139
+ database: vanity_test
140
+ YML
141
+ end
142
+
143
+ assert_equal "mongodb://localhost:27017/vanity_test", load_rails("", <<-RB, "mongodb")
144
+ $stdout << Vanity.playground.connection
145
+ RB
146
+
147
+ assert_equal "Mongo::ReplSetConnection", load_rails("", <<-RB, "mongodb")
148
+ $stdout << Vanity.playground.connection.mongo.class
149
+ RB
150
+ ensure
151
+ File.unlink "tmp/config/vanity.yml"
152
+ end
153
+ end
154
+ end
155
+
156
+ def test_connection_from_yaml_missing
157
+ FileUtils.mkpath "tmp/config"
158
+ File.open("tmp/config/vanity.yml", "w") do |io|
159
+ io.write <<-YML
160
+ production:
161
+ adapter: redis
162
+ YML
163
+ end
164
+
165
+ assert_equal "No configuration for development", load_rails("\nbegin\n", <<-RB, "development")
166
+ rescue RuntimeError => e
167
+ $stdout << e.message
168
+ end
169
+ RB
170
+ ensure
171
+ File.unlink "tmp/config/vanity.yml"
172
+ end
173
+
174
+ def test_collection_from_vanity_yaml
175
+ FileUtils.mkpath "tmp/config"
176
+ File.open("tmp/config/vanity.yml", "w") do |io|
177
+ io.write <<-YML
178
+ production:
179
+ collecting: false
180
+ adapter: mock
181
+ YML
182
+ end
183
+ assert_equal "false", load_rails("", <<-RB)
184
+ $stdout << Vanity.playground.collecting?
185
+ RB
186
+ ensure
187
+ File.unlink "tmp/config/vanity.yml"
188
+ end
189
+
190
+ def test_collection_true_in_production_by_default
191
+ assert_equal "true", load_rails("", <<-RB)
192
+ $stdout << Vanity.playground.collecting?
193
+ RB
194
+ end
195
+
196
+ def test_collection_false_in_production_when_configured
197
+ assert_equal "false", load_rails("\nVanity.playground.collecting = false\n", <<-RB)
198
+ $stdout << Vanity.playground.collecting?
199
+ RB
200
+ end
201
+
202
+ def test_collection_true_in_development_by_default
203
+ assert_equal "true", load_rails("", <<-RB, "development")
204
+ $stdout << Vanity.playground.collecting?
205
+ RB
206
+ end
207
+
208
+ def test_collection_true_in_development_when_configured
209
+ assert_equal "true", load_rails("\nVanity.playground.collecting = true\n", <<-RB, "development")
210
+ $stdout << Vanity.playground.collecting?
211
+ RB
212
+ end
213
+
214
+ def test_collection_false_after_test!
215
+ assert_equal "false", load_rails("", <<-RB)
216
+ Vanity.playground.test!
217
+ $stdout << Vanity.playground.collecting?
218
+ RB
219
+ end
220
+
221
+ def test_playground_loads_if_connected
222
+ assert_equal "{}", load_rails("", <<-RB)
223
+ $stdout << Vanity.playground.instance_variable_get(:@experiments).inspect
224
+ RB
225
+ end
226
+
227
+ def test_playground_does_not_load_if_not_connected
228
+ ENV['VANITY_DISABLED'] = '1'
229
+ assert_equal "nil", load_rails("", <<-RB)
230
+ $stdout << Vanity.playground.instance_variable_get(:@experiments).inspect
231
+ RB
232
+ ENV['VANITY_DISABLED'] = nil
233
+ end
234
+
235
+ def load_rails(before_initialize, after_initialize, env="production")
236
+ tmp = Tempfile.open("test.rb")
237
+ begin
238
+ code_setup = <<-RB
239
+ $:.delete_if { |path| path[/gems\\/vanity-\\d/] }
240
+ $:.unshift File.expand_path("../lib")
241
+ RAILS_ROOT = File.expand_path(".")
242
+ RB
243
+ code = code_setup
244
+ code += load_rails_3_or_4(env)
245
+ code += %Q{\nrequire "vanity"\n}
246
+ code += before_initialize
247
+ code += initialize_rails_3_or_4
248
+ code += after_initialize
249
+ tmp.write code
250
+ tmp.flush
251
+ Dir.chdir "tmp" do
252
+ open("| ruby #{tmp.path}").read
253
+ end
254
+ ensure
255
+ tmp.close!
256
+ end
257
+ end
258
+
259
+ def load_rails_3_or_4(env)
260
+ <<-RB
261
+ ENV['BUNDLE_GEMFILE'] ||= "#{ENV['BUNDLE_GEMFILE']}"
262
+ require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
263
+ ENV['RAILS_ENV'] = ENV['RACK_ENV'] = "#{env}"
264
+ require "active_model/railtie"
265
+ require "action_controller/railtie"
266
+
267
+ Bundler.require(:default)
268
+
269
+ module Foo
270
+ class Application < Rails::Application
271
+ config.active_support.deprecation = :notify
272
+ config.eager_load = #{env == "production"} if Rails::Application.respond_to?(:eager_load!)
273
+ ActiveSupport::Deprecation.silenced = true if ActiveSupport::Deprecation.respond_to?(:silenced) && ENV['CI']
274
+ end
275
+ end
276
+ RB
277
+ end
278
+
279
+ def initialize_rails_3_or_4
280
+ <<-RB
281
+ Foo::Application.initialize!
282
+ RB
283
+ end
284
+
285
+ end
data/test/helper_test.rb CHANGED
@@ -1,14 +1,17 @@
1
- require "test/test_helper"
1
+ require "test_helper"
2
2
 
3
- context "Object#track!" do
4
- test "identity option sets identity" do
5
- metric "Coolness"
6
- new_ab_test :foobar do
7
- alternatives "foo", "bar"
8
- metrics :coolness
9
- end
10
- track! :coolness, :identity=>'quux', :values=>[2]
3
+ describe Object do
4
+ describe "#track!" do
5
+ it "identity option sets identity" do
6
+ metric "Coolness"
7
+ new_ab_test :foobar do
8
+ alternatives "foo", "bar"
9
+ metrics :coolness
10
+ end
11
+ track! :coolness, :identity=>'quux', :values=>[2]
11
12
 
12
- assert_equal 2, experiment(:foobar).alternatives.sum(&:conversions)
13
+ # experiment(:foobar).alternatives.sum(&:conversions).must_equal 2
14
+ assert_equal 2, experiment(:foobar).alternatives.sum(&:conversions)
15
+ end
13
16
  end
14
17
  end