hornsby 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Lachie Cox
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,154 @@
1
+ = Hornsby
2
+
3
+ A Scenario Builder without the fixture pain.
4
+
5
+ == Usage
6
+
7
+ Scenarios look like:
8
+
9
+ scenario :apple do
10
+ @apple = Fruit.create! :species => 'apple'
11
+ end
12
+
13
+ scenario :orange do
14
+ @orange = Fruit.create! :species => 'orange'
15
+ end
16
+
17
+ scenario :fruitbowl => [:apple,:orange] do
18
+ @fruitbowl = FruitBowl.create! :fruit => [@apple,@orange]
19
+ end
20
+
21
+ scenario :kitchen => :fruitbowl do
22
+ @kitchen = Kitchen.create! :fruitbowl => @fruitbowl
23
+ end
24
+
25
+ ...and you use them in specs like:
26
+
27
+ describe Fruit, "@apple" do
28
+ before do
29
+ hornsby_scenario :apple
30
+ end
31
+
32
+ it "should be an apple" do
33
+ @apple.species.should == 'apple'
34
+ end
35
+ end
36
+
37
+ describe FruitBowl, "with and apple and an orange" do
38
+ before do
39
+ hornsby_scenario :fruitbowl
40
+ end
41
+
42
+ it "should have 2 fruits" do
43
+ @fruitbowl.should have(2).fruit
44
+ end
45
+ end
46
+
47
+ All scenarios are run only once, no matter how many times they were called, meaning that you don't need to worry about
48
+ duplicating data.
49
+
50
+ There's also a possibility to delete preloaded data with hornsby_clear. When called without arguments it will drop all data.
51
+ You can also pass it table names, that will be cleared of any data. Beware that any scenarios already executed will still be
52
+ marked as executed, so you won't be able to execute them again. If you want to execute those scenarios later in test, you
53
+ can pass :undo option with list of scenarios to mark as not executed or :all if you want to mark that no scenario has been executed.
54
+
55
+ hornsby_clear :fruits, :trees # Deletes trees and fruits tables
56
+ hornsby_clear # Deletes all data except tables that are defined by Hornsby.skip_tables
57
+ hornsby_clear :fruits, :undo => :apples # Deletes fruits table and marks :apples scenario as not executed
58
+ hornsby_clear :undo => :all # Deletes all tables and marks all scenario as not executed (fresh start)
59
+
60
+ Hornsby searches for scenario files in this particular order in Rails (Merb) root:
61
+ * hornsby_scenarios.rb
62
+ * hornsby_scenarios/*.rb
63
+ * hornsby_scenario.rb
64
+ * hornsby_scenario/*.rb
65
+ * spec/hornsby_scenarios.rb
66
+ * spec/hornsby_scenarios/*.rb
67
+ * spec/hornsby_scenario.rb
68
+ * spec/hornsby_scenario/*.rb
69
+ * test/hornsby_scenarios.rb
70
+ * test/hornsby_scenarios/*.rb
71
+ * test/hornsby_scenario.rb
72
+ * test/hornsby_scenario/*.rb
73
+ You can pass :root option to override framework root and :filename option to pass custom filename pattern
74
+
75
+ == Setup
76
+
77
+ The easiest way to install this gem for Ruby on Rails is just add this line to config/environment.rb (or config/environments/test.rb):
78
+
79
+ config.gem 'sinsiliux-hornsby', :lib => 'hornsby', :source => 'http://gems.github.com'
80
+
81
+ If you’re not using rails, then you can install it through command line
82
+
83
+ gem sources -a http://gems.github.com
84
+ sudo gem install sinsiliux-hornsby
85
+
86
+ Lastly you could use it as plugin:
87
+
88
+ ruby script/plugin install git://github.com/sinsiliux/hornsby.git
89
+
90
+ Hornsby scenarios is activated by calling enable_hornsby. For specifics on how to call that in your testing framework see a little lower.
91
+ enable_hornsby supports these parameters:
92
+ * root - custom framework root if automatic detection fails for some reason (eg. not rails/merb project)
93
+ * filename - custom files pattern with hornsby scenarios
94
+ * scenarios - list of hornsby scenarios that should be preloaded (available in all tests, never reloaded so they're much faster)
95
+
96
+ === RSpec
97
+
98
+ Add the following to spec_helper.rb
99
+
100
+ Spec::Runner.configure do |config|
101
+ ...
102
+
103
+ config.enable_hornsby :filename => 'scenarios.rb', :scenarios => :preloaded_scenario
104
+ end
105
+
106
+ === Test::Unit
107
+
108
+ Add the following lines to test_helper.rb
109
+
110
+ class ActiveSupport::TestCase
111
+ ...
112
+
113
+ enable_hornsby
114
+ end
115
+
116
+ == Advanced Usage
117
+
118
+ Its just ruby, right? So go nuts:
119
+
120
+ 1.upto(9) do |i|
121
+ scenario("user_#{i}") do
122
+ user = User.create! :name => "user#{i}"
123
+ instance_variable_set("@user_#{i}",user)
124
+ end
125
+ end
126
+
127
+ == Transactions
128
+
129
+ Hornsby scenarios is transactional, meaning that after every test transaction is dropped and database is reset to
130
+ starting point. Starting point is empty database + any scenarios that you specify in configure_rspec.
131
+
132
+ == Rake
133
+
134
+ If you'd like simply to load your scenarios into a database, use the rake task like so:
135
+
136
+ $ rake hornsby:scenario RAILS_ENV=test SCENARIO=fruitbowl
137
+
138
+ == TODO
139
+
140
+ * Add scenario namespaces for better organisation.
141
+ * Add ability to revert one scenario.
142
+ * Add preloading scenarios for whole block of tests.
143
+
144
+ == Credits
145
+
146
+ Lachie Cox <lachie@smartbomb.com.au>
147
+
148
+ Andrius Chamentauskas <sinsiliux@gmail.com>
149
+
150
+ The code is based on Err's code found in this post: http://errtheblog.com/post/7708
151
+
152
+ == License
153
+
154
+ MIT, see LICENCE
@@ -0,0 +1,159 @@
1
+ require File.join(File.dirname(__FILE__), 'hornsby/context')
2
+ require File.join(File.dirname(__FILE__), 'hornsby/helper')
3
+ require File.join(File.dirname(__FILE__), 'hornsby/errors')
4
+ if defined? Spec
5
+ require File.join(File.dirname(__FILE__), 'hornsby/rspec_extensions')
6
+ else
7
+ require File.join(File.dirname(__FILE__), 'hornsby/test_unit_extensions')
8
+ end
9
+
10
+ class Hornsby
11
+ SCENARIO_FILES = [nil, "spec", "test"].map do |dir|
12
+ ["hornsby_scenarios", "hornsby_scenario"].map do |file|
13
+ path = File.join([dir, file].compact)
14
+ ["#{path}.rb", File.join(path, "*.rb")]
15
+ end
16
+ end.flatten
17
+
18
+ @@delete_sql = "DELETE FROM %s"
19
+
20
+ cattr_reader :scenarios
21
+ cattr_accessor :executed_scenarios
22
+ # @@namespaces = {}
23
+ @@scenarios = {}
24
+ @@executed_scenarios = Set.new
25
+ @@global_executed_scenarios = []
26
+
27
+ @@global_context = Hornsby::Context
28
+ @@context = nil
29
+
30
+ def self.framework_root
31
+ @@framework_root ||= RAILS_ROOT rescue Rails.root rescue Merb.root rescue nil
32
+ end
33
+
34
+ def self.configure_rspec(config, options = {})
35
+ raise '### This is deprecated! Please use config.enable_hornsby instead of Hornsby.configure_rspec(config)'
36
+ end
37
+
38
+ def self.setup(current_context)
39
+ @@context = @@global_context.clone
40
+ @@executed_scenarios = Set.new(@@global_executed_scenarios)
41
+ copy_ivars(current_context, true)
42
+ ActiveRecord::Base.connection.increment_open_transactions
43
+ ActiveRecord::Base.connection.transaction_joinable = false
44
+ ActiveRecord::Base.connection.begin_db_transaction
45
+ end
46
+
47
+ def self.teardown
48
+ ActiveRecord::Base.connection.rollback_db_transaction
49
+ ActiveRecord::Base.connection.decrement_open_transactions
50
+ end
51
+
52
+ def self.build(*names)
53
+ scenarios = names.map {|name| @@scenarios[name.to_sym] or raise ScenarioNotFoundError, name}
54
+
55
+ scenarios.each {|s| s.build}
56
+ end
57
+
58
+ def self.load(options = {})
59
+ return unless @@scenarios.empty?
60
+
61
+ delete_tables
62
+ @@framework_root = options[:root] if options[:root]
63
+ load_scenarios_files(options[:filename] || SCENARIO_FILES)
64
+
65
+ @@context = @@global_context
66
+ @@global_scenarios = Hornsby.build(*options[:scenarios]) if options[:scenarios]
67
+ @@global_executed_scenarios = @@executed_scenarios.to_a
68
+ end
69
+
70
+ def self.load_scenarios_files(*patterns)
71
+ patterns.flatten!
72
+ patterns.collect! {|pattern| File.join(framework_root, pattern)} if framework_root
73
+
74
+ patterns.each do |pattern|
75
+ unless (files = Dir.glob(pattern)).empty?
76
+ files.each{|f| self.module_eval File.read(f)}
77
+ return
78
+ end
79
+ end
80
+
81
+ raise "Scenarios file not found! Put scenarios in #{patterns.join(' or ')} or pass custom filename with :filename option"
82
+ end
83
+
84
+ def self.scenario(scenario, &block)
85
+ self.new(scenario, &block)
86
+ end
87
+
88
+ def self.delete_tables(*args)
89
+ args = tables if args.blank?
90
+ args.each { |t| ActiveRecord::Base.connection.delete(@@delete_sql % t) }
91
+ end
92
+
93
+ def self.tables
94
+ ActiveRecord::Base.connection.tables - skip_tables
95
+ end
96
+
97
+ def self.skip_tables
98
+ %w( schema_info schema_migrations )
99
+ end
100
+
101
+ def self.copy_ivars(to, reload = false)
102
+ @@context.copy_ivars(to, reload)
103
+ end
104
+
105
+ attr_reader :scenario
106
+
107
+ def initialize(scenario, &block)
108
+ @scenario, @parents = parse_name(scenario)
109
+ @block = block
110
+
111
+ @@scenarios[@scenario] = self
112
+ end
113
+
114
+ def parse_name(name)
115
+ case name
116
+ when Hash
117
+ return name.keys.first.to_sym, [name.values.first].flatten.map{|sc| parse_name(sc).first}
118
+ when Symbol, String
119
+ return name.to_sym, []
120
+ else
121
+ raise TypeError, "Pass scenarios names as strings or symbols only, cannot build scenario '#{name.inspect}'"
122
+ end
123
+ end
124
+
125
+ def say(*messages)
126
+ puts messages.map { |message| "=> #{message}" }
127
+ end
128
+
129
+ def build
130
+ build_parent_scenarios(@@context)
131
+ build_scenario(@@context)
132
+ self
133
+ end
134
+
135
+ def build_scenario(context)
136
+ surface_errors { context.execute(&@block) } unless @@executed_scenarios.include?(@scenario)
137
+ @@executed_scenarios << @scenario
138
+ end
139
+
140
+ def build_parent_scenarios(context)
141
+ @parents.each do |p|
142
+ parent = @@scenarios[p] or raise ScenarioNotFoundError, p
143
+
144
+ parent.build_parent_scenarios(context)
145
+ parent.build_scenario(context)
146
+ end
147
+ end
148
+
149
+ def surface_errors
150
+ yield
151
+ rescue StandardError => error
152
+ puts
153
+ say "There was an error building scenario '#{@scenario}'", error.inspect
154
+ puts
155
+ puts error.backtrace
156
+ puts
157
+ raise error
158
+ end
159
+ end
@@ -0,0 +1,15 @@
1
+ class Hornsby
2
+ module Context
3
+ def self.execute(&block)
4
+ module_eval(&block) if block
5
+ end
6
+
7
+ def self.copy_ivars(to, reload = false)
8
+ instance_variables.each do |iv|
9
+ v = instance_variable_get(iv)
10
+ v.reload if reload and v.respond_to?(:reload)
11
+ to.instance_variable_set(iv, v)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ class Hornsby
2
+ class ScenarioNotFoundError < NameError
3
+ def initialize(*args)
4
+ @scenarios = args
5
+ end
6
+
7
+ def to_s
8
+ "Scenario(s) not found '#{@scenarios.join(',')}'"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ class Hornsby
2
+ module Helper
3
+ def hornsby_scenario(*names)
4
+ Hornsby.build(*names)
5
+ Hornsby.copy_ivars(self)
6
+ end
7
+
8
+ alias :hornsby_scenarios :hornsby_scenario
9
+
10
+ def hornsby_clear(*args)
11
+ options = args.extract_options!
12
+ Hornsby.delete_tables(*args)
13
+
14
+ if options[:undo] == :all
15
+ Hornsby.executed_scenarios.clear
16
+ else
17
+ undo = [options[:undo]].flatten.compact
18
+ unless (not_found = undo - Hornsby.executed_scenarios.to_a).blank?
19
+ raise(ArgumentError, "Scenario(s) #{not_found} not found")
20
+ end
21
+ Hornsby.executed_scenarios -= undo
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ module Spec
2
+ module Runner
3
+ class Configuration
4
+ def enable_hornsby(options = {})
5
+ Hornsby.load(options)
6
+
7
+ include(Hornsby::Helper)
8
+ before do
9
+ Hornsby.setup(self)
10
+ end
11
+ after do
12
+ Hornsby.teardown
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Test
2
+ module Unit
3
+ class TestCase
4
+ def run_with_hornsby(result, &progress_block)
5
+ Hornsby.setup(self)
6
+ run_without_hornsby(result, &progress_block)
7
+ Hornsby.teardown
8
+ end
9
+
10
+ def self.enable_hornsby(options = {})
11
+ include Hornsby::Helper
12
+ Hornsby.load(options)
13
+ alias_method_chain :run, :hornsby
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__)+'/../hornsby'
2
+
3
+ namespace :hornsby do
4
+
5
+ desc "Load the scenario named in the env var SCENARIO"
6
+ task :scenario => :environment do
7
+ raise "set SCENARIO to define which scenario to load" unless ENV['SCENARIO']
8
+ ::Hornsby.load
9
+ ::Hornsby.build(ENV['SCENARIO'])
10
+ end
11
+
12
+ end
@@ -0,0 +1,8 @@
1
+ test:
2
+ adapter: mysql
3
+ socket: /var/run/mysqld/mysqld.sock
4
+ encoding: utf8
5
+ database: hornsby_test
6
+ username: root
7
+ password:
8
+ host: localhost
@@ -0,0 +1,2 @@
1
+ class Fruit < ActiveRecord::Base
2
+ end
@@ -0,0 +1,10 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :fruits, :force => true do |t|
3
+ t.string :species
4
+ t.integer :average_diameter
5
+ end
6
+
7
+ create_table :trees, :force => true do |t|
8
+ t.string :name
9
+ end
10
+ end
@@ -0,0 +1,46 @@
1
+ scenario(:just_apple) do
2
+ @apple = Fruit.create! :species => 'apple'
3
+ end
4
+
5
+ scenario(:many_apples => [:just_apple, :just_apple, :just_apple])
6
+
7
+ scenario(:bananas_and_apples => :just_apple) do
8
+ @banana = Fruit.create! :species => 'banana'
9
+ end
10
+
11
+ scenario(:just_orange) do
12
+ @orange = Fruit.create! :species => 'orange'
13
+ end
14
+
15
+ scenario(:fruit => [:just_apple,:just_orange]) do
16
+ @fruit = [@orange,@apple]
17
+ end
18
+
19
+ scenario(:bananas_and_apples_and_oranges => [:bananas_and_apples,:just_orange]) do
20
+ @fruit = [@orange,@apple,@banana]
21
+ end
22
+
23
+ scenario(:cherry) do
24
+ @cherry = Fruit.create! :species => 'cherry', :average_diameter => 3
25
+ end
26
+
27
+ scenario(:big_cherry => :cherry) do
28
+ @big_cherry = Fruit.create! :species => @cherry.species, :average_diameter => 7
29
+ end
30
+
31
+ scenario(:cherry_basket => [:big_cherry, :cherry]) do
32
+ @basket = [@cherry, @big_cherry]
33
+ end
34
+
35
+
36
+ scenario :parent_not_existing => :not_existing
37
+
38
+ # Hornsby.namespace(:pitted_fruit) do
39
+ # scenario(:peach) do
40
+ # @peach = Fruit.create! :species => 'peach'
41
+ # end
42
+ #
43
+ # scenario(:nectarine) do
44
+ # @nectarine = Fruit.create! :species => 'nectarine'
45
+ # end
46
+ # end
@@ -0,0 +1,194 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Hornsby do
4
+ describe "scenario files" do
5
+ it "should be loaded from specified dirs" do
6
+ Hornsby::SCENARIO_FILES.should == ["hornsby_scenarios.rb", "hornsby_scenarios/*.rb", "hornsby_scenario.rb", "hornsby_scenario/*.rb", "spec/hornsby_scenarios.rb", "spec/hornsby_scenarios/*.rb", "spec/hornsby_scenario.rb", "spec/hornsby_scenario/*.rb", "test/hornsby_scenarios.rb", "test/hornsby_scenarios/*.rb", "test/hornsby_scenario.rb", "test/hornsby_scenario/*.rb"]
7
+ end
8
+ end
9
+
10
+ describe "with just_apple scenario" do
11
+ before do
12
+ hornsby_scenario :just_apple
13
+ end
14
+
15
+ it "should create @apple" do
16
+ @apple.should_not be_nil
17
+ end
18
+
19
+ it "should create Fruit @apple" do
20
+ @apple.should be_instance_of(Fruit)
21
+ end
22
+
23
+ it "should not create @banana" do
24
+ @banana.should be_nil
25
+ end
26
+
27
+ it "should have correct species" do
28
+ @apple.species.should == 'apple'
29
+ end
30
+ end
31
+
32
+ describe "with bananas_and_apples scenario" do
33
+ before do
34
+ hornsby_scenario :bananas_and_apples
35
+ end
36
+
37
+ it "should have correct @apple species" do
38
+ @apple.species.should == 'apple'
39
+ end
40
+
41
+ it "should have correct @banana species" do
42
+ @banana.species.should == 'banana'
43
+ end
44
+ end
45
+
46
+ describe "with fruit scenario" do
47
+ before do
48
+ hornsby_scenario :fruit
49
+ end
50
+
51
+ it "should have 2 fruits" do
52
+ @fruit.should have(2).items
53
+ end
54
+
55
+ it "should have an @apple" do
56
+ @apple.species.should == 'apple'
57
+ end
58
+
59
+ it "should have an @orange" do
60
+ @orange.species.should == 'orange'
61
+ end
62
+
63
+ it "should have no @banana" do
64
+ @banana.should be_nil
65
+ end
66
+ end
67
+
68
+ describe 'with preloaded cherry scenario' do
69
+ it "should have correct size after changed by second test" do
70
+ @cherry.average_diameter.should == 3
71
+ @cherry.update_attribute(:average_diameter, 1)
72
+ @cherry.average_diameter.should == 1
73
+ end
74
+
75
+ it "should have correct size" do
76
+ @cherry.average_diameter.should == 3
77
+ @cherry.update_attribute(:average_diameter, 5)
78
+ @cherry.average_diameter.should == 5
79
+ end
80
+
81
+ it "should create big cherry" do
82
+ @big_cherry.species.should == 'cherry'
83
+ end
84
+ end
85
+
86
+ describe 'hornsby_clear' do
87
+ before do
88
+ hornsby_scenario :just_apple
89
+ end
90
+
91
+ it "should clear scenarios when calling hornsby_clear" do
92
+ hornsby_clear
93
+ Fruit.count.should == 0
94
+ end
95
+
96
+ it "should clear only tables passed" do
97
+ Tree.create!(:name => 'oak')
98
+ hornsby_clear :fruits
99
+ Tree.count.should == 1
100
+ Fruit.count.should == 0
101
+ end
102
+
103
+ it "should mark scenarios as undone when passed :undone option" do
104
+ hornsby_scenario :fruit
105
+ hornsby_clear :undo => [:just_apple]
106
+ Fruit.count.should == 0
107
+ hornsby_scenario :fruit
108
+ Fruit.count.should == 1
109
+ end
110
+
111
+ it "should mark all scenarios as undone when passed :undone option as :all" do
112
+ hornsby_scenario :fruit
113
+ hornsby_clear :undo => :all
114
+ Fruit.count.should == 0
115
+ hornsby_scenario :fruit
116
+ Fruit.count.should == 2
117
+ end
118
+
119
+ it "should raise error when not executed scenarios passed to :undo option" do
120
+ lambda {
121
+ hornsby_clear :undo => :just_orange
122
+ }.should raise_error(ArgumentError)
123
+ end
124
+ end
125
+
126
+ describe 'with many apples scenario' do
127
+ before do
128
+ hornsby_scenario :many_apples, :cherry, :cherry_basket
129
+ end
130
+
131
+ it "should create only one apple" do
132
+ Fruit.all(:conditions => 'species = "apple"').size.should == 1
133
+ end
134
+
135
+ it "should create only two cherries even if they were preloaded" do
136
+ Fruit.all(:conditions => 'species = "cherry"').size.should == 2
137
+ end
138
+
139
+ it "should contain cherries in basket if basket is loaded in test and cherries preloaded" do
140
+ @basket.should == [@cherry, @big_cherry]
141
+ end
142
+ end
143
+
144
+ describe 'transactions' do
145
+ before do
146
+ hornsby_scenario :just_apple
147
+ end
148
+
149
+ it "should drop only inner transaction" do
150
+ @apple.reload.should_not be_nil
151
+ begin
152
+ ActiveRecord::Base.transaction do
153
+ f = Fruit.create(:species => 'orange')
154
+ f.reload.should_not be_nil
155
+ raise 'some error'
156
+ end
157
+ rescue
158
+ end
159
+ @apple.reload.should_not be_nil
160
+ Fruit.find_by_species('orange').should be_nil
161
+ end
162
+ end
163
+
164
+ describe 'errors' do
165
+ it 'should raise ScenarioNotFoundError when scenario could not be found' do
166
+ lambda {
167
+ hornsby_scenario :not_existing
168
+ }.should raise_error(Hornsby::ScenarioNotFoundError, "Scenario(s) not found 'not_existing'")
169
+ end
170
+
171
+ it 'should raise ScenarioNotFoundError when scenario parent could not be found' do
172
+ lambda {
173
+ hornsby_scenario :parent_not_existing
174
+ }.should raise_error(Hornsby::ScenarioNotFoundError, "Scenario(s) not found 'not_existing'")
175
+ end
176
+
177
+ it 'should raise TypeError when scenario name is not symbol or string' do
178
+ lambda {
179
+ Hornsby.new(1)
180
+ }.should raise_error(TypeError, "Pass scenarios names as strings or symbols only, cannot build scenario '1'")
181
+ end
182
+ end
183
+
184
+ #describe "with pitted namespace" do
185
+ # before do
186
+ # Hornsby.build('pitted:peach').copy_ivars(self)
187
+ # end
188
+
189
+ # it "should have @peach" do
190
+ # @peach.species.should == 'peach'
191
+ # end
192
+ #end
193
+ end
194
+
@@ -0,0 +1,24 @@
1
+ require 'fileutils'
2
+ require 'activerecord'
3
+ begin
4
+ require 'mysqlplus'
5
+ rescue LoadError
6
+ end
7
+
8
+ spec_dir = File.dirname(__FILE__)
9
+ Dir.chdir spec_dir
10
+
11
+ ActiveRecord::Base.logger = Logger.new(File.join(spec_dir, "..", "debug.log"))
12
+
13
+ databases = YAML::load(IO.read("db/database.yml"))
14
+ db_info = databases[ENV["DB"] || "test"]
15
+ ActiveRecord::Base.establish_connection(db_info)
16
+
17
+ require 'spec/autorun'
18
+ require '../lib/hornsby'
19
+ require 'db/fruit'
20
+ require 'db/tree'
21
+
22
+ Spec::Runner.configure do |config|
23
+ config.enable_hornsby(:root => File.expand_path(File.join(spec_dir, '..')), :scenarios => :big_cherry)
24
+ end
@@ -0,0 +1,195 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+ require 'shoulda'
3
+
4
+ class HornsbyTest < ActiveSupport::TestCase
5
+ context "scenario files" do
6
+ should "be loaded from specified dirs" do
7
+ assert(Hornsby::SCENARIO_FILES == ["hornsby_scenarios.rb", "hornsby_scenarios/*.rb", "hornsby_scenario.rb", "hornsby_scenario/*.rb", "spec/hornsby_scenarios.rb", "spec/hornsby_scenarios/*.rb", "spec/hornsby_scenario.rb", "spec/hornsby_scenario/*.rb", "test/hornsby_scenarios.rb", "test/hornsby_scenarios/*.rb", "test/hornsby_scenario.rb", "test/hornsby_scenario/*.rb"])
8
+ end
9
+ end
10
+
11
+ context "with just_apple scenario" do
12
+ setup do
13
+ hornsby_scenario :just_apple
14
+ end
15
+
16
+ should "create @apple" do
17
+ assert(!(@apple.nil?))
18
+ end
19
+
20
+ should "create Fruit @apple" do
21
+ assert(@apple.instance_of?(Fruit))
22
+ end
23
+
24
+ should "not create @banana" do
25
+ assert(@banana.nil?)
26
+ end
27
+
28
+ should "have correct species" do
29
+ assert(@apple.species == 'apple')
30
+ end
31
+ end
32
+
33
+ context "with bananas_and_apples scenario" do
34
+ setup do
35
+ hornsby_scenario :bananas_and_apples
36
+ end
37
+
38
+ should "have correct @apple species" do
39
+ assert(@apple.species == 'apple')
40
+ end
41
+
42
+ should "have correct @banana species" do
43
+ assert(@banana.species == 'banana')
44
+ end
45
+ end
46
+
47
+ context "with fruit scenario" do
48
+ setup do
49
+ hornsby_scenario :fruit
50
+ end
51
+
52
+ should "have 2 fruits" do
53
+ assert(@fruit.size == 2)
54
+ end
55
+
56
+ should "have an @apple" do
57
+ assert(@apple.species == 'apple')
58
+ end
59
+
60
+ should "have an @orange" do
61
+ assert(@orange.species == 'orange')
62
+ end
63
+
64
+ should "have no @banana" do
65
+ assert(@banana.nil?)
66
+ end
67
+ end
68
+
69
+ context 'with preloaded cherry scenario' do
70
+ should "have correct size after changed by second test" do
71
+ assert(@cherry.average_diameter == 3)
72
+ @cherry.update_attribute(:average_diameter, 1)
73
+ assert(@cherry.average_diameter == 1)
74
+ end
75
+
76
+ should "have correct size" do
77
+ assert(@cherry.average_diameter == 3)
78
+ @cherry.update_attribute(:average_diameter, 5)
79
+ assert(@cherry.average_diameter == 5)
80
+ end
81
+
82
+ should "create big cherry" do
83
+ assert(@big_cherry.species == 'cherry')
84
+ end
85
+ end
86
+
87
+ context 'hornsby_clear' do
88
+ setup do
89
+ hornsby_scenario :just_apple
90
+ end
91
+
92
+ should "clear scenarios when calling hornsby_clear" do
93
+ hornsby_clear
94
+ assert(Fruit.count == 0)
95
+ end
96
+
97
+ should "clear only tables passed" do
98
+ Tree.create!(:name => 'oak')
99
+ hornsby_clear :fruits
100
+ assert(Tree.count == 1)
101
+ assert(Fruit.count == 0)
102
+ end
103
+
104
+ should "mark scenarios as undone when passed :undone option" do
105
+ hornsby_scenario :fruit
106
+ hornsby_clear :undo => [:just_apple]
107
+ assert(Fruit.count == 0)
108
+ hornsby_scenario :fruit
109
+ assert(Fruit.count == 1)
110
+ end
111
+
112
+ should "mark all scenarios as undone when passed :undone option as :all" do
113
+ hornsby_scenario :fruit
114
+ hornsby_clear :undo => :all
115
+ assert(Fruit.count == 0)
116
+ hornsby_scenario :fruit
117
+ assert(Fruit.count == 2)
118
+ end
119
+
120
+ should "raise error when not executed scenarios passed to :undo option" do
121
+ assert_raise(ArgumentError) do
122
+ hornsby_clear :undo => :just_orange
123
+ end
124
+ end
125
+ end
126
+
127
+ context 'with many apples scenario' do
128
+ setup do
129
+ hornsby_scenario :many_apples, :cherry, :cherry_basket
130
+ end
131
+
132
+ should "create only one apple" do
133
+ assert(Fruit.all(:conditions => 'species = "apple"').size == 1)
134
+ end
135
+
136
+ should "create only two cherries even if they were preloaded" do
137
+ assert(Fruit.all(:conditions => 'species = "cherry"').size == 2)
138
+ end
139
+
140
+ should "contain cherries in basket if basket is loaded in test and cherries preloaded" do
141
+ assert(@basket == [@cherry, @big_cherry])
142
+ end
143
+ end
144
+
145
+ context 'transactions' do
146
+ setup do
147
+ hornsby_scenario :just_apple
148
+ end
149
+
150
+ should "drop only inner transaction" do
151
+ assert(!(@apple.reload.nil?))
152
+ begin
153
+ ActiveRecord::Base.transaction do
154
+ f = Fruit.create(:species => 'orange')
155
+ assert(!(f.reload.nil?))
156
+ raise 'some error'
157
+ end
158
+ rescue
159
+ end
160
+ assert(!(@apple.reload.nil?))
161
+ assert(Fruit.find_by_species('orange').nil?)
162
+ end
163
+ end
164
+
165
+ context 'errors' do
166
+ should 'raise ScenarioNotFoundError when scenario could not be found' do
167
+ assert_raise(Hornsby::ScenarioNotFoundError, "Scenario(s) not found 'not_existing'") do
168
+ hornsby_scenario :not_existing
169
+ end
170
+ end
171
+
172
+ should 'raise ScenarioNotFoundError when scenario parent could not be found' do
173
+ assert_raise(Hornsby::ScenarioNotFoundError, "Scenario(s) not found 'not_existing'") do
174
+ hornsby_scenario :parent_not_existing
175
+ end
176
+ end
177
+
178
+ should 'raise TypeError when scenario name is not symbol or string' do
179
+ assert_raise(TypeError, "Pass scenarios names as strings or symbols only, cannot build scenario '1'") do
180
+ Hornsby.new(1)
181
+ end
182
+ end
183
+ end
184
+
185
+ #describe "with pitted namespace" do
186
+ # before do
187
+ # Hornsby.build('pitted:peach').copy_ivars(self)
188
+ # end
189
+
190
+ # it "should have @peach" do
191
+ # @peach.species.should == 'peach'
192
+ # end
193
+ #end
194
+ end
195
+
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'fileutils'
3
+ require 'activerecord'
4
+ require 'test/unit'
5
+ require 'active_record/test_case'
6
+ begin
7
+ require 'mysqlplus'
8
+ rescue LoadError
9
+ end
10
+
11
+ spec_dir = File.join(File.dirname(__FILE__), '..', 'spec')
12
+
13
+ ActiveRecord::Base.logger = Logger.new(File.join(spec_dir, "..", "debug.log"))
14
+
15
+ databases = YAML::load(IO.read(spec_dir + "/db/database.yml"))
16
+ db_info = databases[ENV["DB"] || "test"]
17
+ ActiveRecord::Base.establish_connection(db_info)
18
+
19
+ require spec_dir + '/../lib/hornsby'
20
+ require spec_dir + '/db/fruit'
21
+ require spec_dir + '/db/tree'
22
+
23
+ class ActiveSupport::TestCase
24
+ enable_hornsby :root => File.join(File.dirname(__FILE__), '..'), :scenarios => :big_cherry
25
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hornsby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.2
5
+ platform: ruby
6
+ authors:
7
+ - Andrius Chamentauskas
8
+ - Lachie Cox
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-10-12 00:00:00 +03:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activerecord
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 2.0.0
25
+ version:
26
+ description:
27
+ email: sinsiliux@gmail.com
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files: []
33
+
34
+ files:
35
+ - lib/hornsby.rb
36
+ - lib/hornsby/context.rb
37
+ - lib/hornsby/helper.rb
38
+ - lib/hornsby/errors.rb
39
+ - lib/hornsby/rspec_extensions.rb
40
+ - lib/hornsby/test_unit_extensions.rb
41
+ - lib/tasks/hornsby_tasks.rake
42
+ - README.rdoc
43
+ - LICENSE
44
+ has_rdoc: true
45
+ homepage: http://github.com/sinsiliux/hornsby
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options: []
50
+
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.3.5
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Fixtures replacement with scenarios
72
+ test_files:
73
+ - spec/spec_helper.rb
74
+ - spec/hornsby_spec.rb
75
+ - spec/hornsby_scenario.rb
76
+ - spec/db/fruit.rb
77
+ - spec/db/database.yml.example
78
+ - spec/db/schema.rb
79
+ - test/test_helper.rb
80
+ - test/hornsby_test.rb