coulda 0.4.7 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -64,3 +64,9 @@ Cleanup
64
64
  0.4.7
65
65
  -----
66
66
  - Aded a newline after a Feature has been printed via the coulda:print_features task
67
+
68
+ 0.5.0
69
+ -----
70
+ - Coulda::Features created from the Kernel::Feature method now create and return a subclass of Test::Unit::TestCase (or a subclass of the specified :testcase_class).
71
+ - The block passed to Kernel::Feature changes self to be the Test::Unit::TestCase subclass. That way, it's virtually identical to a "class Foo < Test::Unit::TestCase"
72
+ - Removed dependency on Jeremy McAnally's "pending" gem in favor of an implementation targetted specifically to Coulda's needs (File names and line numbers of the Given/When/Then/And that is unimplemented)
@@ -55,4 +55,4 @@ Thanks to
55
55
  ---------
56
56
  * David Chelimsky and Jim Weirich for turning me into the test-obsessed developer that I am.
57
57
  * Timothy King for putting up with my scotch-powered rambling while working on v0.4.0
58
- * We Are Titans (http://www.wearetitans.net/) for sponsoring v0.4.0
58
+ * We Are Titans (http://www.wearetitans.net/) for sponsoring v0.4.0 on up
data/Rakefile CHANGED
@@ -24,7 +24,6 @@ begin
24
24
  require 'jeweler'
25
25
  Jeweler::Tasks.new do |s|
26
26
  s.name = "coulda"
27
- s.version = "0.1.1"
28
27
  s.authors = ["Evan David Light"]
29
28
  s.email = "evan@tiggerpalace.com"
30
29
  s.summary = "Behaviour Driven Development derived from Cucumber but as an internal DSL with methods for reuse"
@@ -32,7 +31,7 @@ begin
32
31
  s.description = "Behaviour Driven Development derived from Cucumber but as an internal DSL with methods for reuse"
33
32
  end
34
33
  rescue LoadError
35
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
34
+ puts "Jeweler not available. Install it with: sudo gem install jeweler -s http://gems.github.com"
36
35
  end
37
36
 
38
37
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.7
1
+ 0.5.0
@@ -1,15 +1,15 @@
1
1
  # Generated by jeweler
2
- # DO NOT EDIT THIS FILE
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{coulda}
8
- s.version = "0.4.7"
8
+ s.version = "0.5.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Evan David Light"]
12
- s.date = %q{2009-11-30}
12
+ s.date = %q{2009-12-04}
13
13
  s.description = %q{Behaviour Driven Development derived from Cucumber but as an internal DSL with methods for reuse}
14
14
  s.email = %q{evan@tiggerpalace.com}
15
15
  s.extra_rdoc_files = [
@@ -34,18 +34,20 @@ Gem::Specification.new do |s|
34
34
  "lib/coulda.rb",
35
35
  "lib/coulda/feature.rb",
36
36
  "lib/coulda/scenario.rb",
37
- "lib/coulda/world.rb",
38
37
  "lib/coulda/tasks.rb",
38
+ "lib/coulda/pending.rb",
39
39
  "lib/coulda/tasks/print_features.rake",
40
40
  "lib/coulda/vendor/constantize.rb",
41
41
  "lib/coulda/vendor/underscore.rb",
42
+ "lib/coulda/world.rb",
42
43
  "test/feature_test.rb",
43
44
  "test/integration/using_coulda_test.rb",
44
45
  "test/regression/issue_1_test.rb",
46
+ "test/regression/pending_scenarios_test.rb",
45
47
  "test/scenario_test.rb",
46
48
  "test/test_helper.rb"
47
49
  ]
48
- s.homepage = %q{http://coulda.tiggerpalace.com/}
50
+ s.homepage = %q{http://evan.tiggerpalace.com/}
49
51
  s.rdoc_options = ["--charset=UTF-8"]
50
52
  s.require_paths = ["lib"]
51
53
  s.rubygems_version = %q{1.3.5}
@@ -54,6 +56,7 @@ Gem::Specification.new do |s|
54
56
  "test/feature_test.rb",
55
57
  "test/integration/using_coulda_test.rb",
56
58
  "test/regression/issue_1_test.rb",
59
+ "test/regression/pending_scenarios_test.rb",
57
60
  "test/scenario_test.rb",
58
61
  "test/test_helper.rb"
59
62
  ]
@@ -69,3 +72,4 @@ Gem::Specification.new do |s|
69
72
  else
70
73
  end
71
74
  end
75
+
@@ -4,21 +4,33 @@ end
4
4
 
5
5
  require 'test/unit'
6
6
 
7
- gem 'pending', '>= 0.1.1'
8
- require 'pending'
9
-
10
7
  require 'coulda/world'
11
8
  require 'coulda/feature'
12
9
  require 'coulda/scenario'
10
+ require 'coulda/pending'
13
11
  require 'coulda/vendor/constantize'
14
12
  require 'coulda/vendor/underscore'
15
13
 
16
14
  module Kernel
15
+ # Factory method for Test::Unit::TestCase subclasses
17
16
  def Feature(name, opts = {}, &block)
18
- f = Feature.new(name, opts)
19
- f.instance_eval &block if block_given?
20
- f.assert_presence_of_intent
21
- f
17
+ test_class = Class.new(opts[:testcase_class] || Test::Unit::TestCase)
18
+ Coulda::assign_class_to_const test_class, name
19
+ test_class.class_eval &block if block_given?
20
+ test_class.assert_presence_of_intent
21
+ World.register_feature(test_class, name)
22
+ test_class
23
+ end
24
+ end
25
+
26
+ module Coulda
27
+ def assign_class_to_const(test_class, potential_const)
28
+ base_name = potential_const
29
+ if potential_const !~ /^[a-zA-Z]/
30
+ base_name = "a_" + base_name
31
+ end
32
+ titleized_underscored_name = base_name.super_custom_underscore.gsub(/\b('?[a-z])/) { $1.upcase }
33
+ Object.const_set(titleized_underscored_name, test_class)
22
34
  end
23
35
  end
24
36
 
@@ -1,63 +1,57 @@
1
- module Coulda
2
- # The Feature class is composed of an intent and a series of zero or more Scenarios that describe the behavior
3
- # that satisfies the intent. Capturing intent is a key feature of Coulda. Intent defines the business value
4
- # the Feature is attempting to fulfill. It serves as a reminder for developers (and customers) as to why this
5
- # Feature was implemented and why this spec was written.
6
- class Feature
7
- include Test::Unit::Assertions
8
-
9
- attr_reader :scenarios, :name
10
- attr_accessor :current_scenario, :test_instance
11
-
12
- def initialize(name, opts = {})
13
- World.register_feature(self)
14
- @name = name
15
- @scenarios = []
16
- Scenario.testcase_class = opts[:testcase_class] if opts[:testcase_class]
17
- end
18
-
19
- %w[in_order_to as_a i_want_to].each do |intent|
20
- eval <<-HERE
21
- # An intent specifier
22
- def #{intent}(val = nil)
23
- val ? @#{intent} = val : @#{intent}
24
- end
25
- HERE
26
- end
1
+ module Test
2
+ module Unit
3
+ class TestCase
4
+ include Coulda
5
+
6
+ def self.scenarios
7
+ @scenarios ||= []
8
+ @scenarios
9
+ end
27
10
 
28
- %w[Given When Then And].each do |stmt|
29
- eval <<-HERE
30
- # Specifies a prereqisite, event, or expectation. May be used in any order within the spec
31
- def #{stmt}(text, &block)
32
- @current_scenario.statements << { :type => :#{stmt}, :text => text }
33
- @current_scenario.steps << block if block
34
- end
35
- HERE
36
- end
11
+ def self.current_scenario=(scenario)
12
+ @current_scenario = scenario
13
+ end
37
14
 
38
- # Creates a Scenario instance and adds it to the Feature
39
- def Scenario(scenario_name, &block)
40
- @scenarios << scenario = Scenario.new(scenario_name, self, &block)
41
- scenario
42
- end
15
+ def self.current_scenario
16
+ @current_scenario
17
+ end
43
18
 
44
- # Accessor to the Scenario-created TestCases
45
- def tests
46
- @scenarios.collect { |s| s.test_class }
47
- end
19
+ %w[in_order_to as_a i_want_to].each do |intent|
20
+ eval <<-HERE
21
+ # An intent specifier
22
+ def self.#{intent}(val = nil)
23
+ val ? @#{intent} = val : @#{intent}
24
+ end
25
+ HERE
26
+ end
48
27
 
28
+ %w[Given When Then And].each do |stmt|
29
+ eval <<-HERE
30
+ # Specifies a prereqisite, event, or expectation. May be used in any order within the spec
31
+ def self.#{stmt}(text, &block)
32
+ step = nil
33
+ if block_given?
34
+ current_scenario.steps << step = block
35
+ end
36
+ caller[0] =~ (/(.*):(.*)(:in)?/)
37
+ current_scenario.statements << { :type => :#{stmt}, :text => text, :block => step, :file => $1, :line => $2}
38
+ end
39
+ HERE
40
+ end
49
41
 
50
- # Raises an error if only some of the intent is captured (i.e., in_order_to and as_a without i_want_to)
51
- def assert_presence_of_intent
52
- presence = %w[in_order_to as_a i_want_to].map { |intent| instance_variable_get("@#{intent}") }
53
- if presence.any? { |p| p } && !presence.all? { |p| p }
54
- raise SyntaxError.new("Must have all or none of in_order, as_a, and i_want_to called in a Feature")
42
+ # Creates a Scenario instance and adds it to the Feature
43
+ def self.Scenario(scenario_name, &block)
44
+ @scenarios ||=[]
45
+ @scenarios << scenario = Scenario.new(scenario_name, self, &block)
46
+ scenario
55
47
  end
56
- end
57
48
 
58
- def method_missing(name, *args)
59
- if test_instance
60
- test_instance.__send__(name, *args)
49
+ # Raises an error if only some of the intent is captured (i.e., in_order_to and as_a without i_want_to)
50
+ def self.assert_presence_of_intent
51
+ presence = %w[in_order_to as_a i_want_to].map { |intent| instance_variable_get("@#{intent}") }
52
+ if presence.any? { |p| p } && !presence.all? { |p| p }
53
+ raise SyntaxError.new("Must have all or none of in_order, as_a, and i_want_to called in a Feature")
54
+ end
61
55
  end
62
56
  end
63
57
  end
@@ -0,0 +1,25 @@
1
+ module Test
2
+ module Unit
3
+ class TestCase
4
+ @@pending_cases = []
5
+ @@at_exit = false
6
+
7
+ # Loosely based upon Jeremy McAnally's pending
8
+
9
+ def pending(scenario, statement)
10
+ @@pending_cases << [scenario, statement]
11
+ print "P"
12
+
13
+ @@at_exit ||= begin
14
+ at_exit do
15
+ puts "\nPending Cases:"
16
+ @@pending_cases.each do |scenario, stmt|
17
+ puts "#{stmt[:file]}:#{stmt[:line]}: Scenario '#{scenario.name}': #{stmt[:type]} '#{stmt[:text]}'"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -1,33 +1,18 @@
1
1
  module Coulda
2
- # A factory for Test::Unit::TestCases (or their subclass).
2
+ # A factory for Test::Unit::TestCase test methods
3
3
  class Scenario
4
4
  attr_reader :name, :test_class
5
5
  attr_accessor :steps, :statements
6
6
 
7
- # Class-level method to set the subclass of Test::Unit::TestCase to use as the parent for
8
- # tests manufactured by Scenarios.
9
- def self.testcase_class=(klass)
10
- unless klass.ancestors.include?(Test::Unit::TestCase)
11
- raise Exception.new("class must inherit from Test::Unit:TestCase")
12
- end
13
- @testcase_class = klass
14
- end
15
-
16
- def self.testcase_class
17
- @testcase_class
18
- end
19
-
20
7
  def initialize(name, my_feature, &block)
21
8
  raise Exception.new("Scenario must have a name") unless name
22
9
  @name = name
23
10
  @statements = []
24
11
  @steps = []
25
12
  @pending = false
26
- @test_class = ::Class.new(Scenario.testcase_class || Test::Unit::TestCase)
27
- assign_test_class_to_const(my_feature)
28
- test_impl = nil
29
- if block
30
- create_and_provision_test_method_for my_feature, &block
13
+ @my_feature = my_feature
14
+ if block_given?
15
+ create_and_provision_test_method_using &block
31
16
  else
32
17
  @pending = true
33
18
  define_test_method_using { pending }
@@ -41,40 +26,31 @@ module Coulda
41
26
 
42
27
  private
43
28
 
44
- def create_and_provision_test_method_for(feature, &block)
45
- execute block, :within => feature
46
- inject_test_steps_into @test_class
29
+ def create_and_provision_test_method_using(&block)
30
+ collect_scenario_statements_from &block
47
31
  define_test_method_using do
48
- feature.test_instance = self
49
- self.class.test_steps.each do |s|
50
- feature.instance_eval &s
32
+ self.class.current_scenario.statements.each do |stmt|
33
+ if stmt[:block]
34
+ self.instance_eval &(stmt[:block])
35
+ else
36
+ pending self.class.current_scenario, stmt
37
+ break
38
+ end
51
39
  end
52
- feature.test_instance = nil
53
40
  end
54
41
  end
55
42
 
56
- def assign_test_class_to_const(my_feature)
57
- base_name = "#{my_feature.name}_#{@name}_#{rand(1_000_000_000)}"
58
- base_name = "letter_" + base_name if base_name =~ /^[^a-zA-Z]/
59
- titleized_underscored_name = base_name.super_custom_underscore.gsub(/\b('?[a-z])/) { $1.upcase }
60
- ::Module.const_set(titleized_underscored_name, @test_class)
43
+ def collect_scenario_statements_from(&block)
44
+ @my_feature.current_scenario = self
45
+ @my_feature.instance_eval &block
61
46
  end
62
47
 
63
48
  def define_test_method_using(&block)
64
- @test_class.send(:define_method, "test_#{@name.downcase.super_custom_underscore}", &block)
65
- end
66
-
67
- def execute(block, params = {})
68
- feature = params[:within]
69
- feature.current_scenario = self
70
- feature.instance_eval &block
71
- end
72
-
73
- def inject_test_steps_into(test_class)
74
- class << test_class
75
- attr_accessor :test_steps
49
+ scenario = self
50
+ @my_feature.send(:define_method, "test_#{@name.downcase.super_custom_underscore}") do
51
+ self.class.current_scenario = scenario
52
+ self.instance_eval &block
76
53
  end
77
- test_class.test_steps = @steps
78
54
  end
79
55
  end
80
56
  end
@@ -14,8 +14,8 @@ namespace :coulda do
14
14
  load file
15
15
  end
16
16
 
17
- Coulda::World.features.each do |feature|
18
- puts "Feature: #{feature.name}"
17
+ Coulda::World.features.each do |feature, name|
18
+ puts "Feature: #{name}"
19
19
  puts " In order to #{feature.in_order_to}" if feature.in_order_to
20
20
  puts " As a #{feature.as_a}" if feature.as_a
21
21
  puts " I want to #{feature.i_want_to}" if feature.i_want_to
@@ -1,7 +1,7 @@
1
1
  module Coulda
2
2
  class World
3
- def self.register_feature(feature)
4
- (@features ||= []) << feature
3
+ def self.register_feature(feature, name)
4
+ (@features ||= []) << [feature, name]
5
5
  end
6
6
 
7
7
  def self.features
@@ -1,7 +1,15 @@
1
1
  require File.join(File.dirname(__FILE__), "test_helper")
2
2
 
3
3
  class FeatureTest < Test::Unit::TestCase
4
+
5
+ should "be able to subclass any child class of TestCase" do
6
+ MyTestCase = Class.new(Test::Unit::TestCase)
7
+ feature = Feature "a child class", :testcase_class => MyTestCase
8
+ assert feature.ancestors.include? Test::Unit::TestCase
9
+ end
10
+
4
11
  context "A Feature" do
12
+ @@counter = 1
5
13
  setup do
6
14
  @feature = Feature "foobarblech#{@@counter}" do
7
15
  Scenario "" do
@@ -9,10 +17,11 @@ class FeatureTest < Test::Unit::TestCase
9
17
  Then "" do; end
10
18
  end
11
19
  end
20
+ @@counter += 1
12
21
  end
13
22
 
14
- should "contain tests" do
15
- assert(@feature.tests.first)
23
+ should "be a Test::Unit::TestCase by default" do
24
+ assert @feature.ancestors.include? Test::Unit::TestCase
16
25
  end
17
26
 
18
27
  %w[Given When Then And].each do |condition|
@@ -75,20 +84,18 @@ class FeatureTest < Test::Unit::TestCase
75
84
  end
76
85
  end
77
86
 
78
- should "not have any test" do
79
- assert(@feature_without_scenarios.tests.empty?)
87
+ should "not have any tests" do
88
+ assert @feature_without_scenarios.instance_methods.grep(/^test_/).empty?
80
89
  end
81
90
  end
82
91
 
83
92
  context "that does not have any errors" do
84
- @@counter = 1
85
93
  setup do
86
94
  @feature_without_errors = Feature @@counter.to_s do
87
95
  in_order_to "foo"
88
96
  as_a "bar"
89
97
  i_want_to "blech"
90
98
  end
91
- @@counter += 1
92
99
  end
93
100
 
94
101
  ### Integration tests
@@ -98,7 +105,7 @@ class FeatureTest < Test::Unit::TestCase
98
105
  @feature_without_errors.Scenario("pending scenario") {}
99
106
  test_name = "test_pending_scenario"
100
107
  test_name = test_name.to_sym if RUBY_VERSION =~ /^1.9/
101
- assert(@feature_without_errors.tests.first.instance_methods.include?(test_name), "Test is missing test method from scenario")
108
+ assert(@feature_without_errors.instance_methods.include?(test_name), "Test is missing test method from scenario")
102
109
  end
103
110
 
104
111
  should "create a Scenario" do
@@ -1,6 +1,10 @@
1
1
  require File.join(File.dirname(__FILE__), "..", "test_helper")
2
2
 
3
+ require 'rr'
4
+
3
5
  Feature "Using Coulda", :testcase_class => Test::Unit::TestCase do
6
+ include RR::Adapters::TestUnit
7
+
4
8
  in_order_to "perform lightweight integration/acceptance testing with Coulda"
5
9
  as_a "developer"
6
10
  i_want_to "have typical Coulda usage work"
@@ -10,8 +14,10 @@ Feature "Using Coulda", :testcase_class => Test::Unit::TestCase do
10
14
  end
11
15
 
12
16
  Scenario "A pending scenario with a Given/When/Then without a block" do
13
- Given "this scenario"
14
- When "an event happens" do; end
17
+ Given "this scenario which should be pending" do
18
+ mock(self).pending(anything, anything).times(1)
19
+ end
20
+ When "an event happens"
15
21
  Then "should not error/fail because it is pending" do; end
16
22
  end
17
23
 
@@ -1,5 +1,7 @@
1
1
  require File.join(File.dirname(__FILE__), "..", "test_helper")
2
2
 
3
+ require 'rr'
4
+
3
5
  module GivenSomething
4
6
  def given_something
5
7
  Given "something" do; end
@@ -8,9 +10,11 @@ end
8
10
 
9
11
  # http://github.com/elight/coulda/issues#issue/1
10
12
  class Issue1Test < Test::Unit::TestCase
13
+ include RR::Adapters::TestUnit
14
+
11
15
  context "A Feature that has a Scenario that invokes a 'Given/When/Then' called from a method" do
12
16
  setup do
13
- pendings_are_errors
17
+ stub(self).pending { raise Exception.new }
14
18
 
15
19
  @feature_without_errors = Feature "Issue 1 feature" do
16
20
  extend GivenSomething
@@ -28,7 +32,7 @@ class Issue1Test < Test::Unit::TestCase
28
32
  end
29
33
 
30
34
  should "pass" do
31
- assert(run_feature(@feature_without_errors.tests.first))
35
+ assert(run_feature(@feature_without_errors))
32
36
  end
33
37
  end
34
38
  end
@@ -0,0 +1,31 @@
1
+ require File.join(File.dirname(__FILE__), "..", "test_helper")
2
+ require 'rr'
3
+
4
+ # http://github.com/elight/coulda/issues#issue/1
5
+ class PendingScenariosTest < Test::Unit::TestCase
6
+ include RR::Adapters::TestUnit
7
+
8
+ context "A Scenario that has at least one pending step" do
9
+ @@counter = 0
10
+ setup do
11
+ @scenario = nil
12
+ @feature_without_errors = Feature "with a pending scenario step #{@@counter}" do
13
+ include RR::Adapters::TestUnit
14
+
15
+ @scenario = Scenario "my scenario" do
16
+ Given "something"
17
+ When "the when"
18
+ Then "the then" do
19
+ mock(self).pending
20
+ end
21
+ end
22
+ @@counter += 1
23
+ end
24
+ end
25
+
26
+ should "pass" do
27
+ assert(run_feature(@feature_without_errors))
28
+ end
29
+ end
30
+ end
31
+
@@ -18,15 +18,5 @@ class ScenarioTest < Test::Unit::TestCase
18
18
  end
19
19
  end
20
20
  end
21
-
22
- should "allow Test::Unit::TestCase class to subclass to be set" do
23
- Scenario.testcase_class = Test::Unit::TestCase
24
- end
25
-
26
- should "raise an Exception if provided a class that does not inherit from Test::Unit::TestCase" do
27
- assert_raises Exception do
28
- Scenario.testcase_class = Object
29
- end
30
- end
31
21
  end
32
22
 
@@ -21,7 +21,7 @@ end
21
21
 
22
22
  def pendings_are_errors
23
23
  Feature.class_eval do
24
- def pending
24
+ def pending(*args)
25
25
  raise Exception.new
26
26
  end
27
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coulda
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.7
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan David Light
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-30 00:00:00 -05:00
12
+ date: 2009-12-04 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -49,18 +49,20 @@ files:
49
49
  - lib/coulda.rb
50
50
  - lib/coulda/feature.rb
51
51
  - lib/coulda/scenario.rb
52
- - lib/coulda/world.rb
53
52
  - lib/coulda/tasks.rb
53
+ - lib/coulda/pending.rb
54
54
  - lib/coulda/tasks/print_features.rake
55
55
  - lib/coulda/vendor/constantize.rb
56
56
  - lib/coulda/vendor/underscore.rb
57
+ - lib/coulda/world.rb
57
58
  - test/feature_test.rb
58
59
  - test/integration/using_coulda_test.rb
59
60
  - test/regression/issue_1_test.rb
61
+ - test/regression/pending_scenarios_test.rb
60
62
  - test/scenario_test.rb
61
63
  - test/test_helper.rb
62
64
  has_rdoc: true
63
- homepage: http://coulda.tiggerpalace.com/
65
+ homepage: http://evan.tiggerpalace.com/
64
66
  licenses: []
65
67
 
66
68
  post_install_message:
@@ -91,5 +93,6 @@ test_files:
91
93
  - test/feature_test.rb
92
94
  - test/integration/using_coulda_test.rb
93
95
  - test/regression/issue_1_test.rb
96
+ - test/regression/pending_scenarios_test.rb
94
97
  - test/scenario_test.rb
95
98
  - test/test_helper.rb