attest 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Gemfile +1 -1
  2. data/Gemfile.lock +6 -1
  3. data/README.rdoc +121 -36
  4. data/Rakefile +12 -1
  5. data/VERSION +1 -1
  6. data/attest.gemspec +61 -36
  7. data/bin/attest +18 -24
  8. data/doodle.txt +73 -29
  9. data/examples/{magic_calculator.rb → basic_functionality_example.rb} +42 -8
  10. data/examples/mocha_example.rb +43 -0
  11. data/examples/module_example.rb +49 -0
  12. data/examples/more/{placeholder.rb → multiple_context_example.rb} +0 -0
  13. data/examples/more/nesting/expectations_as_tests_example.rb +20 -0
  14. data/lib/attest.rb +4 -20
  15. data/lib/attest/config.rb +3 -6
  16. data/lib/attest/core_ext/kernel.rb +19 -1
  17. data/lib/attest/core_ext/object.rb +1 -2
  18. data/lib/attest/core_ext/proc.rb +35 -0
  19. data/lib/attest/execution_context.rb +156 -50
  20. data/lib/attest/expectation_result.rb +22 -1
  21. data/lib/attest/interface/output_writer_configurator.rb +25 -0
  22. data/lib/attest/interface/possible_tests_configurator.rb +35 -0
  23. data/lib/attest/interface/test_double_configurator.rb +40 -0
  24. data/lib/attest/output/basic_output_writer.rb +14 -46
  25. data/lib/attest/output/failures_only_output_writer.rb +47 -0
  26. data/lib/attest/output/output_writer.rb +124 -0
  27. data/lib/attest/output/output_writer_interface.rb +24 -0
  28. data/lib/attest/output/test_unit_output_writer.rb +32 -0
  29. data/lib/attest/proc/proc_source_reader.rb +49 -0
  30. data/lib/attest/rake/attesttask.rb +38 -0
  31. data/lib/attest/test_container.rb +15 -4
  32. data/lib/attest/test_loader.rb +28 -0
  33. data/lib/attest/test_object.rb +35 -29
  34. data/lib/attest/test_parser.rb +69 -11
  35. data/lib/trollop.rb +782 -0
  36. data/spec/interface/output_writer_configurator_test.rb +18 -0
  37. data/spec/interface/possible_tests_configurator_test.rb +55 -0
  38. data/spec/interface/test_double_configurator_test.rb +20 -0
  39. data/spec/output/output_writer_test.rb +20 -0
  40. data/spec/tmp/new_require_test.rb +10 -0
  41. metadata +55 -18
  42. data/.gitignore +0 -23
  43. data/examples/standard_calculator.rb +0 -28
  44. data/lib/attest/attest_error.rb +0 -7
  45. data/lib/attest/itself.rb +0 -34
@@ -1,84 +1,190 @@
1
+ require 'attest/expectation_result'
2
+
1
3
  module Attest
2
4
  class ExecutionContext
3
5
  attr_reader :results
4
6
 
5
- def initialize
6
- @results = []
7
- @subject = self
7
+ class << self
8
+ def assertions
9
+ self.instance_methods(false).select{|method_name| method_name =~ /^should.*/ }.inspect
10
+ end
8
11
  end
9
12
 
10
- def should_raise(type=nil, &block)
11
- result = Attest::ExpectationResult.new
12
- begin
13
- if block_given?
14
- yield
15
- end
16
- rescue => e
17
- result.update(:expected_error => e)
18
- if type && type == e.class
19
- result.success
20
- else
21
- result.success
13
+ def initialize(context=nil)
14
+ @results = []
15
+ @subject = self
16
+ @persistent_context = context
17
+ own_instance_variables = self.instance_variables
18
+ context.instance_variables.each do |instance_variable|
19
+ unless own_instance_variables.include? instance_variable
20
+ self.instance_variable_set(instance_variable.to_s, context.instance_variable_get(instance_variable))
22
21
  end
23
22
  end
24
- unless result.success?
25
- result.failure
23
+ end
24
+
25
+ def should_be(expected_lambda, &block)
26
+ with_new_result do |result|
27
+ expected_lambda.call == yield ? result.success : result.failure
26
28
  end
27
- @results << result
28
- self
29
29
  end
30
+ alias :should_be_a :should_be
31
+ alias :should_be_an :should_be
32
+ alias :should_match :should_be
33
+ alias :should_match_a :should_be
34
+ alias :should_match_an :should_be
30
35
 
31
- def with_message(regex)
32
- result = @results.last
33
- if result.success? && result.attributes[:expected_error]
34
- if !(result.attributes[:expected_error].message =~ regex)
35
- result.failure
36
- end
36
+ def should_not_be(expected_lambda, &block)
37
+ with_new_result do |result|
38
+ expected_lambda.call != yield ? result.success : result.failure
37
39
  end
38
- self
39
40
  end
41
+ alias :should_not_be_a :should_not_be
42
+ alias :should_not_be_an :should_not_be
43
+ alias :should_not_match :should_not_be
44
+ alias :should_not_match_a :should_not_be
45
+ alias :should_not_match_an :should_not_be
46
+
47
+ def should_be_same(expected_value, actual_value=nil, &block)
48
+ derive_result_status_from_method(expected_value, actual_value, :"equal?", &block)
49
+ end
50
+ alias :should_be_same_as :should_be_same
51
+
52
+ def should_not_be_same(expected_value, actual_value=nil, &block)
53
+ derive_result_status_from_method_negated(expected_value, actual_value, :"equal?", &block)
54
+ end
55
+ alias :should_not_be_same_as :should_not_be_same
56
+
57
+ def should_equal(expected_value, actual_value=nil, &block)
58
+ derive_result_status_from_method(expected_value, actual_value, :"==", &block)
59
+ end
60
+
61
+ def should_not_equal(expected_value, actual_value=nil, &block)
62
+ derive_result_status_from_method_negated(expected_value, actual_value, :"==", &block)
63
+ end
64
+ alias :should_not_be_equal :should_not_equal
65
+
66
+ def should_be_true(actual_value=nil, &block)
67
+ derive_result_status_from_method(true, actual_value, :"==", &block)
68
+ end
69
+
70
+ def should_not_be_true(actual_value = nil, &block)
71
+ derive_result_status_from_method(false, actual_value, :"==", &block)
72
+ end
73
+ alias :should_be_false :should_not_be_true
40
74
 
41
75
  def should_fail
42
- result = Attest::ExpectationResult.new
43
- result.failure
44
- @results << result
45
- self
76
+ with_new_result do |result|
77
+ result.failure
78
+ end
46
79
  end
80
+ alias :should_not_succeed :should_fail
47
81
 
48
- def should_be_true(&block)
49
- result = Attest::ExpectationResult.new
50
- block_return = yield
51
- block_return ? result.success : result.failure
52
- @results << result
53
- self
82
+ def should_succeed
83
+ with_new_result do |result|
84
+ result.success
85
+ end
54
86
  end
87
+ alias :should_not_fail :should_succeed
88
+ alias :should_pass :should_succeed
55
89
 
56
90
  def should_not_raise(&block)
57
91
  should_raise(&block)
58
- result = @results.last
59
- result.success? ? result.failure : result.success
60
- self
92
+ with_last_result do |result|
93
+ result.success? ? result.failure : result.success
94
+ end
61
95
  end
62
96
 
63
- def should_not_be_true(&block)
64
- should_be_true(&block)
65
- result = @results.last
66
- result.success? ? result.failure : result.success
67
- self
97
+ #the only opt so far is :with_message which takes a regex
98
+ def should_raise(type=nil, opts={}, &block)
99
+ with_new_result do |result|
100
+ begin
101
+ if block_given?
102
+ yield
103
+ end
104
+ rescue => e
105
+ result.update(:expected_error => e)
106
+ if expected_error?(type, opts[:with_message], e)
107
+ result.success
108
+ else
109
+ result.failure
110
+ end
111
+ end
112
+ unless result.success?
113
+ result.failure
114
+ end
115
+ end
68
116
  end
69
117
 
70
118
  #worker methods
71
119
  def create_and_include(module_class)
72
120
  class_name = "#{module_class}Class"
73
- class_instance = Class.new
74
- Object.const_set class_name, class_instance
75
- Object.const_get(class_name).include(Object.const_get("#{module_class}"))
76
- klass = Object.const_get(class_name)
121
+ klass = nil
122
+ begin
123
+ klass = Object.const_get(class_name)
124
+ rescue NameError => e
125
+ class_instance = Class.new
126
+ Object.const_set class_name, class_instance
127
+ Object.const_get(class_name).include(Object.const_get("#{module_class}"))
128
+ klass = Object.const_get(class_name)
129
+ end
77
130
  klass.new
78
131
  end
79
132
 
80
- def nosetup
81
- true
133
+ private
134
+ def derive_result_status_from_method(expected_value, actual_value, method, negated=false, &block)
135
+ with_new_result do |result|
136
+ if block_given?
137
+ method_return = expected_value.send(method, yield)
138
+ method_return = !method_return if negated
139
+ method_return ? result.success : result.failure
140
+ else
141
+ method_return = expected_value.send(method, actual_value)
142
+ method_return = !method_return if negated
143
+ method_return ? result.success : result.failure
144
+ end
145
+ end
146
+ end
147
+
148
+ def derive_result_status_from_method_negated(expected_value, actual_value, method, &block)
149
+ derive_result_status_from_method(expected_value, actual_value, method, true, &block)
150
+ end
151
+
152
+ def source_location
153
+ caller.each_with_index do |stack_line|
154
+ if stack_line[Attest.config.current_file]
155
+ return stack_line[/(.*:\d+):.*/, 1]
156
+ end
157
+ end
158
+ ""
159
+ end
160
+
161
+ def with_new_result
162
+ result = Attest::ExpectationResult.new
163
+ yield result
164
+ result.source_location = source_location
165
+ @results << result
166
+ end
167
+
168
+ def with_last_result
169
+ result = @results.last
170
+ yield result
171
+ result.source_location = source_location
172
+ end
173
+
174
+ def expected_error?(expected_type, expected_message_regex, actual_error)
175
+ if expected_type.nil? && expected_message_regex.nil? || expected_error_type?(expected_type, actual_error.class) && expected_message_regex.nil? || expected_error_type?(expected_type, actual_error.class) && error_message_matches?(expected_message_regex, actual_error.message)
176
+ return true
177
+ else
178
+ return false
179
+ end
180
+ end
181
+
182
+ def expected_error_type?(expected_type, actual_type)
183
+ expected_type == actual_type
184
+ end
185
+
186
+ def error_message_matches?(expected_message_regex, actual_error_message)
187
+ actual_error_message =~ expected_message_regex
82
188
  end
83
189
  end
84
190
  end
@@ -1,12 +1,24 @@
1
1
  module Attest
2
2
  class ExpectationResult
3
+ include Comparable
4
+ class << self
5
+ def status_types
6
+ status_weights.keys
7
+ end
8
+
9
+ def status_weights
10
+ {:success => 1, :failure => 2, :error => 3, :pending => 4, :disabled => 5}
11
+ end
12
+ end
13
+
3
14
  attr_reader :attributes
15
+ attr_accessor :source_location
4
16
  def initialize(attributes={})
5
17
  @outcome = nil
6
18
  @attributes = attributes
7
19
  end
8
20
 
9
- [:success, :failure, :error, :pending].each do |status|
21
+ Attest::ExpectationResult.status_types.each do |status|
10
22
  eval <<-EOT
11
23
  def #{status}
12
24
  @outcome = current_method
@@ -24,5 +36,14 @@ module Attest
24
36
  def update(attributes={})
25
37
  @attributes.merge!(attributes)
26
38
  end
39
+
40
+ def status_weight
41
+ Attest::ExpectationResult.status_weights[status.to_sym]
42
+ end
43
+
44
+ def <=>(another_result)
45
+ return 1 unless another_result
46
+ self.status_weight <=> another_result.status_weight
47
+ end
27
48
  end
28
49
  end
@@ -0,0 +1,25 @@
1
+ require 'attest/output/basic_output_writer'
2
+ require 'attest/output/test_unit_output_writer'
3
+ require 'attest/output/failures_only_output_writer'
4
+
5
+ module Attest
6
+ class OutputWriterConfigurator
7
+ class << self
8
+ def configure(output_writer_identifier)
9
+ output_writer_identifier = output_writer_identifier || default_output_writer_identifier
10
+ raise "You have specified an unknown output writer" unless output_writer_identifiers.include? output_writer_identifier
11
+ output_writer_class = "#{output_writer_identifier}OutputWriter"
12
+ #Attest.config.output_writer = Attest::Output.const_get(output_writer_class).new
13
+ Attest::Output.const_get(output_writer_class).new
14
+ end
15
+
16
+ def default_output_writer_identifier
17
+ "Basic"
18
+ end
19
+
20
+ def output_writer_identifiers
21
+ [default_output_writer_identifier, "TestUnit", "FailuresOnly"]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ module Attest
2
+ class PossibleTestsConfigurator
3
+ class << self
4
+ def configure(included_locations, excluded_locations = nil)
5
+ raise "Need to know location for tests" if included_locations.compact.size == 0
6
+ possible_test_files = included_test_files included_locations
7
+ files_to_ignore = excluded_test_files excluded_locations
8
+ possible_test_files - files_to_ignore
9
+ end
10
+
11
+ def included_test_files(included_locations)
12
+ file_list_from_list_of included_locations
13
+ end
14
+
15
+ def excluded_test_files(excluded_locations)
16
+ return [] if excluded_locations.nil?
17
+ file_list_from_list_of excluded_locations
18
+ end
19
+
20
+ def file_list_from_list_of(locations)
21
+ file_list = []
22
+ locations.compact.each do |location|
23
+ expanded_location = File.expand_path(location)
24
+ file_list << file_list_from_single(expanded_location)
25
+ end
26
+ file_list.flatten
27
+ end
28
+
29
+ def file_list_from_single(location)
30
+ return location if File.file? location
31
+ Dir[File.join(File.expand_path(location), "**/*.rb")].collect { |ruby_file| ruby_file }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ require 'attest/execution_context'
2
+
3
+ module Attest
4
+ class TestDoubleConfigurator
5
+ class << self
6
+ def configure(test_double_identifier)
7
+ test_double_identifier = test_double_identifier || default_test_double_identifier
8
+ raise "You have specified an unsupported test double framework" unless test_double_identifiers.include? test_double_identifier
9
+ self.send(:"configure_#{test_double_identifier}")
10
+ #Attest.config.testdouble = test_double_identifier
11
+ test_double_identifier
12
+ end
13
+
14
+ def configure_mocha
15
+ begin
16
+ #how would this work when bundler is in play
17
+ require "mocha_standalone"
18
+ rescue LoadError => e
19
+ puts "Trying to use mocha for test double functionality, but can't find it!"
20
+ puts "Perhaps you forgot to install the mocha gem."
21
+ exit
22
+ end
23
+ Attest::ExecutionContext.class_eval do
24
+ include Mocha::API # need this so that methods like stub() and mock() can be accessed directly from the execution context
25
+ end
26
+ end
27
+
28
+ def configure_none
29
+ end
30
+
31
+ def default_test_double_identifier
32
+ "mocha"
33
+ end
34
+
35
+ def test_double_identifiers
36
+ [default_test_double_identifier, "none"]
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,31 +1,31 @@
1
+ require 'attest/output/output_writer'
2
+ require 'attest/expectation_result'
3
+
1
4
  module Attest
2
5
  module Output
3
- class BasicOutputWriter
4
- def initialize
5
- @containers = []
6
- end
7
-
8
- def before_everything
9
- end
10
-
11
- def before_context(container)
6
+ class BasicOutputWriter < Attest::Output::OutputWriter
7
+ def before_container(container)
12
8
  previous_container = @containers.last
13
9
  @containers << container
14
10
  puts "#{container.file}:" unless previous_container && previous_container.file == container.file
15
11
  puts " #{ container.description }"
16
12
  end
17
13
 
14
+ def after_container(container)
15
+ puts
16
+ end
17
+
18
18
  def before_test(test_object)
19
19
  print " - #{test_object.description}"
20
20
  end
21
21
 
22
22
  def after_test(test_object)
23
- relevant_result = nil
24
- test_object.results.each do |result|
25
- relevant_result = result if !result.success?
26
- end
23
+ relevant_result = determine_relevant_result test_object
27
24
  print " [#{relevant_result.status.upcase}]" if relevant_result
28
- if relevant_result && relevant_result.error?
25
+ if relevant_result && relevant_result.failure?
26
+ 2.times { puts }
27
+ puts " #{relevant_result.source_location}"
28
+ elsif relevant_result && relevant_result.error?
29
29
  e = relevant_result.attributes[:unexpected_error]
30
30
  2.times { puts }
31
31
  puts " #{e.class}: #{e.message}"
@@ -36,38 +36,6 @@ module Attest
36
36
  end
37
37
  puts
38
38
  end
39
-
40
- def after_context
41
- puts
42
- end
43
-
44
- def summary
45
- return unless @containers.size >= 1
46
- tests, success, failure, error, pending = 0, 0, 0, 0, 0
47
- @containers.each do |container|
48
- container.test_objects.each do |test_object|
49
- tests += 1
50
- test_object.results.each do |result|
51
- if result.success?
52
- success += 1
53
- elsif result.failure?
54
- failure += 1
55
- elsif result.error?
56
- error += 1
57
- elsif result.pending?
58
- pending += 1
59
- else
60
- raise "Errr, WTF!!!"
61
- end
62
- end
63
- end
64
- end
65
- puts
66
- puts "Ran #{tests} tests: #{success} successful, #{failure} failed, #{error} errors, #{pending} pending"
67
- end
68
-
69
- def after_everything
70
- end
71
39
  end
72
40
  end
73
41
  end