blueprints 0.1.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/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.
data/README.rdoc ADDED
@@ -0,0 +1,150 @@
1
+ = Blueprints
2
+
3
+ Another replacement for factories and fixtures that focuses on being DRY and making developers type as little as possible.
4
+
5
+ == Usage
6
+
7
+ Plans look like:
8
+
9
+ plan :apple do
10
+ Fruit.create! :species => 'apple'
11
+ end
12
+
13
+ plan :orange do
14
+ Fruit.create! :species => 'orange'
15
+ end
16
+
17
+ plan :fruitbowl => [:apple, :orange] do
18
+ FruitBowl.create! :fruit => [@apple,@orange]
19
+ end
20
+
21
+ plan :kitchen => :fruitbowl do
22
+ 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
+ build :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
+ build :fruitbowl
40
+ end
41
+
42
+ it "should have 2 fruits" do
43
+ @fruitbowl.should have(2).fruit
44
+ end
45
+ end
46
+
47
+ Result of 'plan' block is evaluated and assigned to instance variable with the same name. You can also assign your own instance variables
48
+ inside 'plan' block and they will be accessible in tests that build this plan.
49
+
50
+ All scenarios are run only once, no matter how many times they were called, meaning that you don't need to worry about
51
+ duplicating data.
52
+
53
+ There's also a possibility to delete preloaded data with demolish. When called without arguments it will drop all data.
54
+ You can also pass it table names, that will be cleared of any data. Beware that any scenarios already executed will still be
55
+ marked as executed, so you won't be able to execute them again. If you want to execute those scenarios later in test, you
56
+ 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.
57
+
58
+ demolish :fruits, :trees # Deletes trees and fruits tables
59
+ demolish # Deletes all data except tables that are defined by Hornsby.skip_tables
60
+ demolish :fruits, :undo => :apples # Deletes fruits table and marks :apples scenario as not executed
61
+ demolish :undo => :all # Deletes all tables and marks all scenario as not executed (fresh start)
62
+
63
+ Blueprints searches for scenario files in this particular order in Rails (Merb) root:
64
+ * blueprints.rb
65
+ * blueprints/*.rb
66
+ * blueprint.rb
67
+ * blueprint/*.rb
68
+ * spec/blueprints.rb
69
+ * spec/blueprints/*.rb
70
+ * spec/blueprint.rb
71
+ * spec/blueprint/*.rb
72
+ * test/blueprints.rb
73
+ * test/blueprints/*.rb
74
+ * test/blueprint.rb
75
+ * test/blueprint/*.rb
76
+ You can pass :root option to override framework root and :filename option to pass custom filename pattern
77
+
78
+ == Setup
79
+
80
+ 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):
81
+
82
+ config.gem 'sinsiliux-blueprints', :lib => 'blueprints', :source => 'http://gems.github.com'
83
+
84
+ If you’re not using rails, then you can install it through command line
85
+
86
+ gem sources -a http://gems.github.com
87
+ sudo gem install sinsiliux-blueprints
88
+
89
+ Lastly you could use it as plugin:
90
+
91
+ ruby script/plugin install git://github.com/sinsiliux/blueprints.git
92
+
93
+ Hornsby scenarios is activated by calling enable_blueprints. For specifics on how to call that in your testing framework see a little lower.
94
+ enable_blueprintssupports these parameters:
95
+ * root - custom framework root if automatic detection fails for some reason (eg. not rails/merb project)
96
+ * filename - custom files pattern with blueprints plans
97
+ * prebuild - list of blueprints plans that should be preloaded (available in all tests, never reloaded so they're much faster)
98
+
99
+ === RSpec
100
+
101
+ Add the following to spec_helper.rb
102
+
103
+ Spec::Runner.configure do |config|
104
+ ...
105
+
106
+ config.enable_blueprints :filename => 'scenarios.rb', :prebuild => :preloaded_scenario
107
+ end
108
+
109
+ === Test::Unit
110
+
111
+ Add the following lines to test_helper.rb
112
+
113
+ class ActiveSupport::TestCase
114
+ ...
115
+
116
+ enable_blueprints
117
+ end
118
+
119
+ == Advanced Usage
120
+
121
+ Its just ruby, right? So go nuts:
122
+
123
+ 1.upto(9) do |i|
124
+ plan("user_#{i}") do
125
+ user = User.create! :name => "user#{i}"
126
+ instance_variable_set("@user_#{i}",user)
127
+ end
128
+ end
129
+
130
+ == Transactions
131
+
132
+ Blueprints is transactional, meaning that after every test transaction is dropped and database is reset to
133
+ starting point. Starting point is empty database + any scenarios that you specify in enable_blueprints.
134
+
135
+ == TODO
136
+
137
+ * Add plan namespaces for better organisation.
138
+ * Add ability to revert one plan.
139
+ * Add preloading plans for whole block of tests.
140
+ * Fix rake tasks
141
+
142
+ == Credits
143
+
144
+ Andrius Chamentauskas <sinsiliux@gmail.com>
145
+
146
+ The code is based on hornsby scenario plugin by Lachie Cox, which is based on Err's code found in this post: http://errtheblog.com/post/7708
147
+
148
+ == License
149
+
150
+ MIT, see LICENCE
data/lib/blueprints.rb ADDED
@@ -0,0 +1,76 @@
1
+ require File.join(File.dirname(__FILE__), 'blueprints/plan')
2
+ require File.join(File.dirname(__FILE__), 'blueprints/file_context')
3
+ require File.join(File.dirname(__FILE__), 'blueprints/helper')
4
+ require File.join(File.dirname(__FILE__), 'blueprints/errors')
5
+ if defined? Spec
6
+ require File.join(File.dirname(__FILE__), 'blueprints/rspec_extensions')
7
+ else
8
+ require File.join(File.dirname(__FILE__), 'blueprints/test_unit_extensions')
9
+ end
10
+
11
+ module Blueprints
12
+ PLAN_FILES = [nil, "spec", "test"].map do |dir|
13
+ ["blueprint", "blueprints"].map do |file|
14
+ path = File.join([dir, file].compact)
15
+ ["#{path}.rb", File.join(path, "*.rb")]
16
+ end
17
+ end.flatten
18
+
19
+ DELETE_POLICIES = {:delete => "DELETE FROM %s", :truncate => "TRUNCATE %s"}
20
+
21
+ def self.framework_root
22
+ @@framework_root ||= RAILS_ROOT rescue Rails.root rescue Merb.root rescue nil
23
+ end
24
+
25
+ def self.setup(current_context)
26
+ Plan.setup
27
+ Plan.copy_ivars(current_context)
28
+ ActiveRecord::Base.connection.increment_open_transactions
29
+ ActiveRecord::Base.connection.transaction_joinable = false
30
+ ActiveRecord::Base.connection.begin_db_transaction
31
+ end
32
+
33
+ def self.teardown
34
+ ActiveRecord::Base.connection.rollback_db_transaction
35
+ ActiveRecord::Base.connection.decrement_open_transactions
36
+ end
37
+
38
+ def self.load(options = {})
39
+ options.assert_valid_keys(:delete_policy, :filename, :prebuild, :root)
40
+ return unless Plan.plans.empty?
41
+
42
+ @@delete_sql = DELETE_POLICIES[options[:delete_policy]] || DELETE_POLICIES[:delete]
43
+ delete_tables
44
+ @@framework_root = options[:root] if options[:root]
45
+ load_scenarios_files(options[:filename] || PLAN_FILES)
46
+
47
+ Plan.prebuild(options[:prebuild])
48
+ end
49
+
50
+ def self.load_scenarios_files(*patterns)
51
+ patterns.flatten!
52
+ patterns.collect! {|pattern| File.join(framework_root, pattern)} if framework_root
53
+
54
+ patterns.each do |pattern|
55
+ unless (files = Dir.glob(pattern)).empty?
56
+ files.each{|f| FileContext.module_eval File.read(f)}
57
+ return
58
+ end
59
+ end
60
+
61
+ raise "Plans file not found! Put plans in #{patterns.join(' or ')} or pass custom filename pattern with :filename option"
62
+ end
63
+
64
+ def self.delete_tables(*args)
65
+ args = tables if args.blank?
66
+ args.each { |t| ActiveRecord::Base.connection.delete(@@delete_sql % t) }
67
+ end
68
+
69
+ def self.tables
70
+ ActiveRecord::Base.connection.tables - skip_tables
71
+ end
72
+
73
+ def self.skip_tables
74
+ %w( schema_info schema_migrations )
75
+ end
76
+ end
@@ -0,0 +1,11 @@
1
+ module Blueprints
2
+ class PlanNotFoundError < NameError
3
+ def initialize(*args)
4
+ @plans = args
5
+ end
6
+
7
+ def to_s
8
+ "Plan(s) not found '#{@plans.join(',')}'"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Blueprints
2
+ module FileContext
3
+ def self.blueprint(plan, &block)
4
+ Plan.new(plan, &block)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ module Blueprints
2
+ module Helper
3
+ def build_plan(*names)
4
+ Plan.build(*names)
5
+ Plan.copy_ivars(self)
6
+ end
7
+
8
+ alias :build :build_plan
9
+
10
+ def demolish(*args)
11
+ options = args.extract_options!
12
+ Blueprints.delete_tables(*args)
13
+
14
+ if options[:undo] == :all
15
+ Plan.executed_plans.clear
16
+ else
17
+ undo = [options[:undo]].flatten.compact
18
+ unless (not_found = undo - Plan.executed_plans.to_a).blank?
19
+ raise(ArgumentError, "Scenario(s) #{not_found} not found")
20
+ end
21
+ Plan.executed_plans -= undo
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,98 @@
1
+ module Blueprints
2
+ class Plan
3
+ cattr_reader :plans
4
+ cattr_accessor :executed_plans
5
+ @@plans = {}
6
+ @@executed_plans = Set.new
7
+ @@global_executed_plans = Set.new
8
+
9
+ @@global_context = Module.new
10
+ @@context = nil
11
+
12
+ def self.setup
13
+ @@context = YAML.load(@@global_context)
14
+ @@executed_plans = @@global_executed_plans.clone
15
+ end
16
+
17
+ def self.copy_ivars(to)
18
+ @@context.instance_variables.each do |iv|
19
+ to.instance_variable_set(iv, @@context.instance_variable_get(iv))
20
+ end
21
+ end
22
+
23
+ def self.prebuild(plans)
24
+ @@context = @@global_context
25
+ @@global_scenarios = Plan.build(plans) if plans
26
+ @@global_executed_plans = @@executed_plans
27
+ @@global_context = YAML.dump(@@global_context)
28
+ end
29
+
30
+ def self.build(*names)
31
+ names.map {|name| @@plans[name.to_sym] or raise PlanNotFoundError, name}.each {|p| p.build}
32
+ end
33
+
34
+ # Instance
35
+
36
+ attr_reader :name
37
+
38
+ def initialize(scenario, &block)
39
+ @name, @parents = parse_name(scenario)
40
+ @block = block
41
+
42
+ @@plans[@name] = self
43
+ end
44
+
45
+ def build
46
+ build_parent_plans(@@context)
47
+ build_plan(@@context)
48
+ end
49
+
50
+ protected
51
+
52
+ def parse_name(name)
53
+ case name
54
+ when Hash
55
+ return name.keys.first.to_sym, [name.values.first].flatten.map{|sc| parse_name(sc).first}
56
+ when Symbol, String
57
+ return name.to_sym, []
58
+ else
59
+ raise TypeError, "Pass plan names as strings or symbols only, cannot build plan #{name.inspect}"
60
+ end
61
+ end
62
+
63
+ def say(*messages)
64
+ puts messages.map { |message| "=> #{message}" }
65
+ end
66
+
67
+ def build_plan(context)
68
+ surface_errors do
69
+ if @block
70
+ result = context.module_eval(&@block)
71
+ iv_name = :"@#{@name}"
72
+ context.instance_variable_set(iv_name, result) unless context.instance_variable_get(iv_name)
73
+ end
74
+ end unless @@executed_plans.include?(@name)
75
+ @@executed_plans << @name
76
+ end
77
+
78
+ def build_parent_plans(context)
79
+ @parents.each do |p|
80
+ parent = @@plans[p] or raise PlanNotFoundError, p
81
+
82
+ parent.build_parent_plans(context)
83
+ parent.build_plan(context)
84
+ end
85
+ end
86
+
87
+ def surface_errors
88
+ yield
89
+ rescue StandardError => error
90
+ puts
91
+ say "There was an error building scenario '#{@name}'", error.inspect
92
+ puts
93
+ puts error.backtrace
94
+ puts
95
+ raise error
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,17 @@
1
+ module Spec
2
+ module Runner
3
+ class Configuration
4
+ def enable_blueprints(options = {})
5
+ Blueprints.load(options)
6
+
7
+ include(Blueprints::Helper)
8
+ before do
9
+ Blueprints.setup(self)
10
+ end
11
+ after do
12
+ Blueprints.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_blueprints(result, &progress_block)
5
+ Blueprints.setup(self)
6
+ run_without_blueprints(result, &progress_block)
7
+ Blueprints.teardown
8
+ end
9
+
10
+ def self.enable_blueprints(options = {})
11
+ include Blueprints::Helper
12
+ Blueprints.load(options)
13
+ alias_method_chain :run, :blueprints
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__)+'/../blueprints'
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,35 @@
1
+ blueprint :apple do
2
+ Fruit.create! :species => 'apple'
3
+ end
4
+
5
+ blueprint :many_apples => [:apple, :apple, :apple]
6
+
7
+ blueprint :bananas_and_apples => :apple do
8
+ @banana = Fruit.create! :species => 'banana'
9
+ end
10
+
11
+ blueprint :orange do
12
+ Fruit.create! :species => 'orange'
13
+ end
14
+
15
+ blueprint :fruit => [:apple, :orange] do
16
+ [@orange, @apple]
17
+ end
18
+
19
+ blueprint :bananas_and_apples_and_oranges => [:bananas_and_apples, :orange] do
20
+ @fruit = [@orange, @apple, @banana]
21
+ end
22
+
23
+ blueprint :cherry do
24
+ Fruit.create! :species => 'cherry', :average_diameter => 3
25
+ end
26
+
27
+ blueprint :big_cherry => :cherry do
28
+ Fruit.create! :species => @cherry.species, :average_diameter => 7
29
+ end
30
+
31
+ blueprint :cherry_basket => [:big_cherry, :cherry] do
32
+ [@cherry, @big_cherry]
33
+ end
34
+
35
+ blueprint :parent_not_existing => :not_existing
@@ -0,0 +1,216 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Blueprints do
4
+ describe "scenario files" do
5
+ it "should be loaded from specified dirs" do
6
+ Blueprints::PLAN_FILES.should == ["blueprint.rb", "blueprint/*.rb", "blueprints.rb", "blueprints/*.rb", "spec/blueprint.rb", "spec/blueprint/*.rb", "spec/blueprints.rb", "spec/blueprints/*.rb", "test/blueprint.rb", "test/blueprint/*.rb", "test/blueprints.rb", "test/blueprints/*.rb"]
7
+ end
8
+ end
9
+
10
+ describe "with apple scenario" do
11
+ before do
12
+ build :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
+ build :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
+ build :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 'demolish' do
87
+ before do
88
+ build :apple
89
+ end
90
+
91
+ it "should clear scenarios when calling demolish" do
92
+ demolish
93
+ Fruit.count.should == 0
94
+ end
95
+
96
+ it "should clear only tables passed" do
97
+ Tree.create!(:name => 'oak')
98
+ demolish :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
+ build :fruit
105
+ demolish :undo => [:apple]
106
+ Fruit.count.should == 0
107
+ build :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
+ build :fruit
113
+ demolish :undo => :all
114
+ Fruit.count.should == 0
115
+ build :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
+ demolish :undo => :orange
122
+ }.should raise_error(ArgumentError)
123
+ end
124
+ end
125
+
126
+ describe 'delete policies' do
127
+ before do
128
+ Blueprints::Plan.plans.should_receive(:empty?).and_return(true)
129
+ Blueprints.should_receive(:load_scenarios_files).with(Blueprints::PLAN_FILES)
130
+ Blueprints::Plan.should_receive(:prebuild).with(nil)
131
+ end
132
+
133
+ it "should allow using custom delete policy" do
134
+ ActiveRecord::Base.connection.should_receive(:delete).with("TRUNCATE fruits")
135
+ ActiveRecord::Base.connection.should_receive(:delete).with("TRUNCATE trees")
136
+
137
+ Blueprints.load(:delete_policy => :truncate)
138
+ end
139
+
140
+ it "should default to :delete policy if unexisting policy given" do
141
+ ActiveRecord::Base.connection.should_receive(:delete).with("DELETE FROM fruits")
142
+ ActiveRecord::Base.connection.should_receive(:delete).with("DELETE FROM trees")
143
+
144
+ Blueprints.load(:delete_policy => :ukndown)
145
+ end
146
+ end
147
+
148
+ describe 'with many apples scenario' do
149
+ before do
150
+ build :many_apples, :cherry, :cherry_basket
151
+ end
152
+
153
+ it "should create only one apple" do
154
+ Fruit.all(:conditions => 'species = "apple"').size.should == 1
155
+ end
156
+
157
+ it "should create only two cherries even if they were preloaded" do
158
+ Fruit.all(:conditions => 'species = "cherry"').size.should == 2
159
+ end
160
+
161
+ it "should contain cherries in basket if basket is loaded in test and cherries preloaded" do
162
+ @cherry_basket.should == [@cherry, @big_cherry]
163
+ end
164
+ end
165
+
166
+ describe 'transactions' do
167
+ before do
168
+ build :apple
169
+ end
170
+
171
+ it "should drop only inner transaction" do
172
+ @apple.reload.should_not be_nil
173
+ begin
174
+ ActiveRecord::Base.transaction do
175
+ f = Fruit.create(:species => 'orange')
176
+ f.reload.should_not be_nil
177
+ raise 'some error'
178
+ end
179
+ rescue
180
+ end
181
+ @apple.reload.should_not be_nil
182
+ Fruit.find_by_species('orange').should be_nil
183
+ end
184
+ end
185
+
186
+ describe 'errors' do
187
+ it 'should raise ScenarioNotFoundError when scenario could not be found' do
188
+ lambda {
189
+ build :not_existing
190
+ }.should raise_error(Blueprints::PlanNotFoundError, "Plan(s) not found 'not_existing'")
191
+ end
192
+
193
+ it 'should raise ScenarioNotFoundError when scenario parent could not be found' do
194
+ lambda {
195
+ build :parent_not_existing
196
+ }.should raise_error(Blueprints::PlanNotFoundError, "Plan(s) not found 'not_existing'")
197
+ end
198
+
199
+ it 'should raise TypeError when scenario name is not symbol or string' do
200
+ lambda {
201
+ Blueprints::Plan.new(1)
202
+ }.should raise_error(TypeError, "Pass plan names as strings or symbols only, cannot build plan 1")
203
+ end
204
+ end
205
+
206
+ #describe "with pitted namespace" do
207
+ # before do
208
+ # Hornsby.build('pitted:peach').copy_ivars(self)
209
+ # end
210
+
211
+ # it "should have @peach" do
212
+ # @peach.species.should == 'peach'
213
+ # end
214
+ #end
215
+ end
216
+
@@ -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
data/spec/db/fruit.rb ADDED
@@ -0,0 +1,2 @@
1
+ class Fruit < ActiveRecord::Base
2
+ end
data/spec/db/schema.rb ADDED
@@ -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,23 @@
1
+ require 'fileutils'
2
+ require 'activerecord'
3
+ begin
4
+ require 'mysqlplus'
5
+ rescue LoadError
6
+ end
7
+
8
+ Dir.chdir File.join(File.dirname(__FILE__), '..')
9
+
10
+ ActiveRecord::Base.logger = Logger.new("debug.log")
11
+
12
+ databases = YAML::load(IO.read("spec/db/database.yml"))
13
+ db_info = databases[ENV["DB"] || "test"]
14
+ ActiveRecord::Base.establish_connection(db_info)
15
+
16
+ require 'spec/autorun'
17
+ require 'lib/blueprints'
18
+ require 'spec/db/fruit'
19
+ require 'spec/db/tree'
20
+
21
+ Spec::Runner.configure do |config|
22
+ config.enable_blueprints :root => File.expand_path(File.join(File.dirname(__FILE__), '..')), :prebuild => :big_cherry
23
+ end
@@ -0,0 +1,194 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class BlueprintsTest < ActiveSupport::TestCase
4
+ context "scenario files" do
5
+ should "be loaded from specified dirs" do
6
+ assert(Blueprints::PLAN_FILES == ["blueprint.rb", "blueprint/*.rb", "blueprints.rb", "blueprints/*.rb", "spec/blueprint.rb", "spec/blueprint/*.rb", "spec/blueprints.rb", "spec/blueprints/*.rb", "test/blueprint.rb", "test/blueprint/*.rb", "test/blueprints.rb", "test/blueprints/*.rb"] )
7
+ end
8
+ end
9
+
10
+ context "with apple scenario" do
11
+ setup do
12
+ build :apple
13
+ end
14
+
15
+ should "create @apple" do
16
+ assert(!(@apple.nil?))
17
+ end
18
+
19
+ should "create Fruit @apple" do
20
+ assert(@apple.instance_of?(Fruit))
21
+ end
22
+
23
+ should "not create @banana" do
24
+ assert(@banana.nil?)
25
+ end
26
+
27
+ should "have correct species" do
28
+ assert(@apple.species == 'apple')
29
+ end
30
+ end
31
+
32
+ context "with bananas_and_apples scenario" do
33
+ setup do
34
+ build :bananas_and_apples
35
+ end
36
+
37
+ should "have correct @apple species" do
38
+ assert(@apple.species == 'apple')
39
+ end
40
+
41
+ should "have correct @banana species" do
42
+ assert(@banana.species == 'banana')
43
+ end
44
+ end
45
+
46
+ context "with fruit scenario" do
47
+ setup do
48
+ build :fruit
49
+ end
50
+
51
+ should "have 2 fruits" do
52
+ assert(@fruit.size == 2)
53
+ end
54
+
55
+ should "have an @apple" do
56
+ assert(@apple.species == 'apple')
57
+ end
58
+
59
+ should "have an @orange" do
60
+ assert(@orange.species == 'orange')
61
+ end
62
+
63
+ should "have no @banana" do
64
+ assert(@banana.nil?)
65
+ end
66
+ end
67
+
68
+ context 'with preloaded cherry scenario' do
69
+ should "have correct size after changed by second test" do
70
+ assert(@cherry.average_diameter == 3)
71
+ @cherry.update_attribute(:average_diameter, 1)
72
+ assert(@cherry.average_diameter == 1)
73
+ end
74
+
75
+ should "have correct size" do
76
+ assert(@cherry.average_diameter == 3)
77
+ @cherry.update_attribute(:average_diameter, 5)
78
+ assert(@cherry.average_diameter == 5)
79
+ end
80
+
81
+ should "create big cherry" do
82
+ assert(@big_cherry.species == 'cherry')
83
+ end
84
+ end
85
+
86
+ context 'demolish' do
87
+ setup do
88
+ build :apple
89
+ end
90
+
91
+ should "clear scenarios when calling demolish" do
92
+ demolish
93
+ assert(Fruit.count == 0)
94
+ end
95
+
96
+ should "clear only tables passed" do
97
+ Tree.create!(:name => 'oak')
98
+ demolish :fruits
99
+ assert(Tree.count == 1)
100
+ assert(Fruit.count == 0)
101
+ end
102
+
103
+ should "mark scenarios as undone when passed :undone option" do
104
+ build :fruit
105
+ demolish :undo => [:apple]
106
+ assert(Fruit.count == 0)
107
+ build :fruit
108
+ assert(Fruit.count == 1)
109
+ end
110
+
111
+ should "mark all scenarios as undone when passed :undone option as :all" do
112
+ build :fruit
113
+ demolish :undo => :all
114
+ assert(Fruit.count == 0)
115
+ build :fruit
116
+ assert(Fruit.count == 2)
117
+ end
118
+
119
+ should "raise error when not executed scenarios passed to :undo option" do
120
+ assert_raise(ArgumentError) do
121
+ demolish :undo => :orange
122
+ end
123
+ end
124
+ end
125
+
126
+ context 'with many apples scenario' do
127
+ setup do
128
+ build :many_apples, :cherry, :cherry_basket
129
+ end
130
+
131
+ should "create only one apple" do
132
+ assert(Fruit.all(:conditions => 'species = "apple"').size == 1)
133
+ end
134
+
135
+ should "create only two cherries even if they were preloaded" do
136
+ assert(Fruit.all(:conditions => 'species = "cherry"').size == 2)
137
+ end
138
+
139
+ should "contain cherries in basket if basket is loaded in test and cherries preloaded" do
140
+ assert(@cherry_basket == [@cherry, @big_cherry])
141
+ end
142
+ end
143
+
144
+ context 'transactions' do
145
+ setup do
146
+ build :apple
147
+ end
148
+
149
+ should "drop only inner transaction" do
150
+ assert(!(@apple.reload.nil?))
151
+ begin
152
+ ActiveRecord::Base.transaction do
153
+ f = Fruit.create(:species => 'orange')
154
+ assert(!(f.reload.nil?))
155
+ raise 'some error'
156
+ end
157
+ rescue
158
+ end
159
+ assert(!(@apple.reload.nil?))
160
+ assert(Fruit.find_by_species('orange').nil?)
161
+ end
162
+ end
163
+
164
+ context 'errors' do
165
+ should 'raise ScenarioNotFoundError when scenario could not be found' do
166
+ assert_raise(Blueprints::PlanNotFoundError, "Plan(s) not found 'not_existing'") do
167
+ build :not_existing
168
+ end
169
+ end
170
+
171
+ should 'raise ScenarioNotFoundError when scenario parent could not be found' do
172
+ assert_raise(Blueprints::PlanNotFoundError, "Plan(s) not found 'not_existing'") do
173
+ build :parent_not_existing
174
+ end
175
+ end
176
+
177
+ should 'raise TypeError when scenario name is not symbol or string' do
178
+ assert_raise(TypeError, "Pass plan names as strings or symbols only, cannot build plan 1") do
179
+ Blueprints::Plan.new(1)
180
+ end
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,25 @@
1
+ require 'rubygems'
2
+ require 'activerecord'
3
+ require 'test/unit'
4
+ require 'active_record/test_case'
5
+ require 'shoulda'
6
+ begin
7
+ require 'mysqlplus'
8
+ rescue LoadError
9
+ end
10
+
11
+ Dir.chdir(File.join(File.dirname(__FILE__), '..'))
12
+
13
+ ActiveRecord::Base.logger = Logger.new("debug.log")
14
+
15
+ databases = YAML::load(IO.read("spec/db/database.yml"))
16
+ db_info = databases[ENV["DB"] || "test"]
17
+ ActiveRecord::Base.establish_connection(db_info)
18
+
19
+ require 'lib/blueprints'
20
+ require 'spec/db/fruit'
21
+ require 'spec/db/tree'
22
+
23
+ class ActiveSupport::TestCase
24
+ enable_blueprints :root => File.join(File.dirname(__FILE__), '..'), :prebuild => :big_cherry
25
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: blueprints
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrius Chamentauskas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-27 00:00:00 +03:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.0.0
24
+ version:
25
+ description:
26
+ email: sinsiliux@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/blueprints.rb
35
+ - lib/blueprints/errors.rb
36
+ - lib/blueprints/file_context.rb
37
+ - lib/blueprints/helper.rb
38
+ - lib/blueprints/plan.rb
39
+ - lib/blueprints/rspec_extensions.rb
40
+ - lib/blueprints/test_unit_extensions.rb
41
+ - lib/tasks/blueprints_tasks.rake
42
+ - README.rdoc
43
+ - LICENSE
44
+ has_rdoc: true
45
+ homepage: http://github.com/sinsiliux/blueprints
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: Another replacement for factories and fixtures
72
+ test_files:
73
+ - spec/spec_helper.rb
74
+ - spec/blueprints_spec.rb
75
+ - spec/blueprints.rb
76
+ - spec/db/fruit.rb
77
+ - spec/db/database.yml.example
78
+ - spec/db/schema.rb
79
+ - test/test_helper.rb
80
+ - test/blueprints_test.rb