attest 0.1.0 → 0.2.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.
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