clean_test 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,169 @@
1
+ require 'faker'
2
+
3
+ module Clean # :nodoc:
4
+ module Test # :nodoc:
5
+ # Public: Provides the ability to vend arbitrary values without using literals, or
6
+ # long calls to Faker. This has two levels of utility:
7
+ #
8
+ # helper methods - #any_number, #any_int, #any_string provide arbitrary primitives
9
+ # to make it clear what numbers, ints, and strings in your tests are
10
+ # relevant. Arbitrary values should use one of these any_ helpers.
11
+ #
12
+ # any sort of any - you can define your own "any" values by using #new_any, which allows you to
13
+ # extend things if you like. Of course, you could just make your own any_method as well.
14
+ #
15
+ # Example:
16
+ #
17
+ # class Person
18
+ # def initialize(first_name,last_name,age)
19
+ # # ...
20
+ # end
21
+ # end
22
+ #
23
+ # test_that "someone under 18 is a minor" {
24
+ # Given {
25
+ # # First name and last name aren't relevant to the test
26
+ # @person = Person.new(any_string,any_string,17)
27
+ # }
28
+ # When {
29
+ # @minor = @person.minor?
30
+ # }
31
+ # Then {
32
+ # assert @minor
33
+ # }
34
+ # }
35
+ #
36
+ # test_that "full_name gives the full name" {
37
+ # Given {
38
+ # # Age isn't relevant; it just needs to be positive
39
+ # @person = Person.new("Dave","Copeland",any_int :positive)
40
+ # }
41
+ # When {
42
+ # @full_name = @person.full_name
43
+ # }
44
+ # Then {
45
+ # assert_equal "Dave Copeland",@full_namej
46
+ # }
47
+ # }
48
+ module Any
49
+ MAX_RAND = 50000 # :nodoc:
50
+
51
+ # Public: Get any number; one that doesn't matter
52
+ #
53
+ # options - options to control what sort of number comes back:
54
+ # :positive - make sure that the number is greater than zero
55
+ # :negative - make sure that the number is less than zero
56
+ def any_number(*options)
57
+ any :number,options
58
+ end
59
+
60
+ # Public: Returns an integer. options is the same as for #any_number
61
+ def any_int(*options)
62
+ any :int,options
63
+ end
64
+
65
+ # Public: Get an arbitrary string of any potential length (the real max is 2048 characters if you don't override it)
66
+ #
67
+ # options - options to control the returned string:
68
+ # :max - the max size of the string you want
69
+ # :min - the minimum size we want to come back
70
+ #
71
+ # Example
72
+ #
73
+ # any_string :max => 255 # => ensure it'll fit into a varchar(255)
74
+ # any_string :min => 1024 # at least 1024 characters
75
+ #
76
+ def any_string(options = {})
77
+ any :string,options
78
+ end
79
+
80
+ # Public: Get a predefined, arbitrary any.
81
+ #
82
+ # sym - the any that has been defined already. By default, the following are defined:
83
+ # :string - does any_string
84
+ # String - does any_string
85
+ # :number - does any_number
86
+ # Numeric - does any_number
87
+ # Float - does any_number
88
+ # :int - does any_int
89
+ # Fixnum - does any_int
90
+ # Integer - does any_int
91
+ # options - whatever options are relevant to the user-defined any
92
+ #
93
+ # Example
94
+ #
95
+ # new_any(:foo) do |options|
96
+ # if options[:bar]
97
+ # 'bar'
98
+ # else
99
+ # 'quux'
100
+ # end
101
+ # end
102
+ #
103
+ # some_foo = any :foo
104
+ # some_other_foo = any :foo, :bar => true
105
+ def any(sym,options = {})
106
+ anies[sym].call(options)
107
+ end
108
+
109
+ # Public: Create a new any that can be retrieved via #any
110
+ #
111
+ # any - the identifer of your new any. Can be anything though a Symbol or classname would be appropriate
112
+ # block - the block that will be called whenever you #any your any.
113
+ def new_any(any,&block)
114
+ anies[any] = block
115
+ end
116
+
117
+ private
118
+
119
+ def anies
120
+ @anies ||= default_anies
121
+ end
122
+
123
+ ANY_STRING = Proc.new do |options| # :nodoc:
124
+ max_size = options[:max] || rand(2048)
125
+ min_size = options[:min] || rand(1024)
126
+ min_size = max_size if min_size > max_size
127
+
128
+ size = rand(max_size)
129
+
130
+ size = min_size if size < min_size
131
+
132
+ string = Faker::Lorem.words(1).join('')
133
+ while string.length < size
134
+ string += Faker::Lorem.words(1).join('')
135
+ end
136
+
137
+ string[0..(max_size-1)]
138
+ end
139
+
140
+ ANY_NUMBER = Proc.new do |options| # :nodoc:
141
+ number = (rand(2 * MAX_RAND) - MAX_RAND).to_f/100.0
142
+ if options.include? :positive
143
+ number + MAX_RAND
144
+ elsif options.include? :negative
145
+ number - MAX_RAND
146
+ else
147
+ number
148
+ end
149
+ end
150
+
151
+ ANY_INT = Proc.new do |options| # :nodoc:
152
+ (ANY_NUMBER.call(options)).to_i
153
+ end
154
+
155
+ def default_anies
156
+ { :string => ANY_STRING,
157
+ String => ANY_STRING,
158
+ :number => ANY_NUMBER,
159
+ Numeric => ANY_NUMBER,
160
+ Float => ANY_NUMBER,
161
+ :int => ANY_INT,
162
+ Fixnum => ANY_INT,
163
+ Integer => ANY_INT,
164
+ }
165
+ end
166
+ end
167
+ end
168
+ end
169
+
@@ -0,0 +1,186 @@
1
+ module Clean # :nodoc:
2
+ module Test # :nodoc:
3
+ # A means of documenting the parts of your test code according
4
+ # to the class "Given/When/Then" system.
5
+ # This does no enforcement of any kind and is merely documentation to make
6
+ # your tests readable.
7
+ #
8
+ # Example:
9
+ #
10
+ # Given {
11
+ # @circle = Circle.new(10)
12
+ # }
13
+ # When {
14
+ # @area = @circle.area
15
+ # }
16
+ # Then {
17
+ # assert_equal 314,@area
18
+ # }
19
+ #
20
+ # There are also two additional methods provided ,#the_test_runs and
21
+ # #mocks_shouldve_been_called to assist with creating readable tests that use mocks. See those
22
+ # methods for an example
23
+ module GivenWhenThen
24
+ # Public: Set up the conditions for the test
25
+ #
26
+ # existing_block - a callable object (e.g. a Proc) that will be called immediately
27
+ # by this Given. If nil, &block is expected to be passed
28
+ # block - a block given to this call that will be executed immediately
29
+ # by this Given. If existing_block is non-nil, this is ignored
30
+ #
31
+ # Examples
32
+ #
33
+ # Given {
34
+ # @foo = "bar"
35
+ # }
36
+ #
37
+ # Returns the block that was executed
38
+ def Given(existing_block=nil,&block)
39
+ call_block_param_or_block_given(existing_block,&block)
40
+ end
41
+ # Public: Execute the code under test
42
+ #
43
+ # existing_block - a callable object (e.g. a Proc) that will be called immediately
44
+ # by this When. If nil, &block is expected to be passed
45
+ # block - a block given to this call that will be executed immediately
46
+ # by this When. If existing_block is non-nil, this is ignored
47
+ #
48
+ # Examples
49
+ #
50
+ # When {
51
+ # @foo.go
52
+ # }
53
+ #
54
+ # Returns the block that was executed
55
+ def When(existing_block=nil,&block)
56
+ Given(existing_block,&block)
57
+ end
58
+
59
+ # Public: Verify the results of the test
60
+ #
61
+ # existing_block - a callable object (e.g. a Proc) that will be called immediately
62
+ # by this Then. If nil, &block is expected to be passed
63
+ # block - a block given to this call that will be executed immediately
64
+ # by this Then. If existing_block is non-nil, this is ignored
65
+ #
66
+ # Examples
67
+ #
68
+ # Then {
69
+ # assert_equal "bar",@foo
70
+ # }
71
+ #
72
+ # Returns the block that was executed
73
+ def Then(existing_block=nil,&block)
74
+ Given(existing_block,&block)
75
+ end
76
+
77
+ # Public: Continue the previous Given/When/Then in a new block. The reason
78
+ # you might do this is if you wanted to use the existing block form of a
79
+ # Given/When/Then, but also need to do something custom. This allows you to
80
+ # write your test more fluently.
81
+ #
82
+ # existing_block - a callable object (e.g. a Proc) that will be called immediately
83
+ # by this Then. If nil, &block is expected to be passed
84
+ # block - a block given to this call that will be executed immediately
85
+ # by this Then. If existing_block is non-nil, this is ignored
86
+ #
87
+ # Examples
88
+ #
89
+ # def circle(radius)
90
+ # lambda { @circle = Circle.new(radius) }
91
+ # end
92
+ #
93
+ # Given circle(10)
94
+ # And {
95
+ # @another_circle = Circle.new(30)
96
+ # }
97
+ #
98
+ # Returns the block that was executed
99
+ def And(existing_block=nil,&block)
100
+ Given(existing_block,&block)
101
+ end
102
+
103
+ # Public: Continue the previous Given/When/Then in a new block. The reason
104
+ # you might do this is if you wanted to use the existing block form of a
105
+ # Given/When/Then, but also need to do something custom, and that custom thing
106
+ # contradicts or overrides the flow, so an "And" wouldn't read properly. This allows you to
107
+ # write your test more fluently.
108
+ #
109
+ # existing_block - a callable object (e.g. a Proc) that will be called immediately
110
+ # by this Then. If nil, &block is expected to be passed
111
+ # block - a block given to this call that will be executed immediately
112
+ # by this Then. If existing_block is non-nil, this is ignored
113
+ #
114
+ # Examples
115
+ #
116
+ # def options
117
+ # lambda {
118
+ # @options = {
119
+ # :foo => 'bar',
120
+ # :quux => 'baz',
121
+ # }
122
+ # }
123
+ # end
124
+ #
125
+ # Given options
126
+ # But {
127
+ # @options[:foo] = 'baz'
128
+ # }
129
+ #
130
+ # Returns the block that was executed
131
+ def But(existing_block=nil,&block)
132
+ Given(existing_block,&block)
133
+ end
134
+
135
+ # Public: Used to make clear the structure of tests using mocks.
136
+ # This returns a no-op, and is intended to be used with a "When"
137
+ # to delineate the creation of a mock in a Given, and the
138
+ # expectations of a mock in a "Then"
139
+ #
140
+ # Example:
141
+ #
142
+ # Given {
143
+ # @google = mock()
144
+ # }
145
+ # When the_test_runs
146
+ # Then {
147
+ # @google.expects(:search).with('foo').returns('bar')
148
+ # }
149
+ # Given {
150
+ # @my_search = Search.new(@google)
151
+ # }
152
+ # When {
153
+ # @result = @my_search.find('foo')
154
+ # }
155
+ # Then {
156
+ # assert_equal 'Found bar',@result
157
+ # }
158
+ # And mocks_shouldve_been_called
159
+ def the_test_runs
160
+ lambda {}
161
+ end
162
+
163
+ # Public: Similar to #test_runs, this is used to make clear what
164
+ # you are testing and what the assertions are. Since many Ruby mock
165
+ # frameworks do not require an explicit "verify" step, you often have tests
166
+ # that have no explicit asserts, the assertions being simply that the mocks were called
167
+ # as expected. This step, which is a no-op, allows you to document that you are
168
+ # expecting mocks to be called. See the example in #mocks_are_called for usage.
169
+ def mocks_shouldve_been_called
170
+ lambda {}
171
+ end
172
+
173
+ private
174
+
175
+ def call_block_param_or_block_given(existing_block,&block)
176
+ if existing_block.nil?
177
+ block.call
178
+ block
179
+ else
180
+ existing_block.call
181
+ existing_block
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,32 @@
1
+ require 'test/unit'
2
+ require 'clean_test/given_when_then'
3
+ require 'clean_test/any'
4
+ require 'clean_test/test_that'
5
+
6
+ module Clean # :nodoc:
7
+ module Test # :nodoc:
8
+ # Public: A Base class brings in all modules that are part
9
+ # of Clean::Test.
10
+ #
11
+ # Example
12
+ #
13
+ # class TestCircle < Clean::Test::TestCase
14
+ # test_that {
15
+ # Given { @circle = Circle.new(10) }
16
+ # When { @area = @circle.area }
17
+ # Then { assert_equal 314,@area }
18
+ # }
19
+ # end
20
+ class TestCase < ::Test::Unit::TestCase
21
+ include GivenWhenThen
22
+ include TestThat
23
+ include Any
24
+ if RUBY_VERSION =~ /^1\.8\./
25
+ # Avoid the stupid behavior of
26
+ # complaining that no tests were specified for 1.8.-like rubies
27
+ def default_test
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,65 @@
1
+ module Clean # :nodoc:
2
+ module Test # :nodoc:
3
+ # Public: Module that, when included, makes a class method, +#test_that+ available
4
+ # to create test methods in a more fluent way. See ClassMethods.
5
+ module TestThat
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ # Public: Create a new test method with the given optional description and
13
+ # body.
14
+ #
15
+ # description - a string describing what is being tested; write this as a follow on
16
+ # from the phrase "test that". If nil, a name will be constructed
17
+ # based on the block
18
+ # block - the body of the test.
19
+ #
20
+ # Example
21
+ #
22
+ # # Create a rails-style test method
23
+ # test_that "area is computed based on positive radius" do
24
+ # Given {
25
+ # @circle = Circle.new(10)
26
+ # }
27
+ # When {
28
+ # @area = @circle.area
29
+ # }
30
+ # Then {
31
+ # assert_equal 314,@area
32
+ # }
33
+ # end
34
+ #
35
+ # # Create an "anonymous" test, where the test body
36
+ # # is clear enough as to what's being tested
37
+ # test_that {
38
+ # Given a_circle(:radius => 10)
39
+ # When get_area
40
+ # Then area_should_be(314)
41
+ # }
42
+ def test_that(description=nil,&block)
43
+ raise "You must provide a block" if block.nil?
44
+ description = make_up_name(block) if description.nil?
45
+ test_name = "test_#{description.gsub(/\s+/,'_')}".to_sym
46
+ defined = instance_method(test_name) rescue false
47
+ raise "#{test_name} is already defined in #{self}" if defined
48
+ define_method(test_name, &block)
49
+ end
50
+
51
+ private
52
+
53
+ def make_up_name(some_proc)
54
+ if some_proc.respond_to? :source_location
55
+ name,location = some_proc.source_location
56
+ "anonymous test at #{name}, line #{location}"
57
+ else
58
+ "anonymous test for proc #{some_proc.object_id}"
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,5 @@
1
+ module Clean # :nodoc:
2
+ module Test # :nodoc:
3
+ VERSION = "0.10.0"
4
+ end
5
+ end
data/test/bootstrap.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter "/test/*.rb"
4
+ end
5
+
data/test/test_any.rb ADDED
@@ -0,0 +1,144 @@
1
+ require 'test/unit'
2
+ require 'clean_test/test_case'
3
+
4
+ class TestAny < Clean::Test::TestCase
5
+ test_that {
6
+ Given { random_seeded_for_negative_float }
7
+ When { @number = any_number }
8
+ Then {
9
+ assert @number < 0,"any_number should be negative, but we got #{@number}"
10
+ assert @number.to_i != @number,"Expected a float"
11
+ }
12
+ }
13
+ test_that {
14
+ Given { random_seeded_for_positive }
15
+ When { @number = any_number }
16
+ Then { assert @number > 0,"any_number should be positive, but we got #{@number}" }
17
+ }
18
+
19
+ test_that {
20
+ Given { random_seeded_for_negative_float }
21
+ When { @number = any_number :positive }
22
+ Then { assert @number > 0,"We specified :positive, but got a negative" }
23
+ }
24
+
25
+ test_that {
26
+ Given { random_seeded_for_positive }
27
+ When { @number = any_number :negative }
28
+ Then { assert @number < 0,"We specified :negative, but got a positive" }
29
+ }
30
+
31
+ test_that {
32
+ Given { random_seeded_for_negative_float }
33
+ When { @number = any_int }
34
+ Then {
35
+ assert @number < 0,"Expected int to be negative"
36
+ assert_equal @number.to_i,@number,"Expected an int, not a #{@number.class}"
37
+ }
38
+ }
39
+
40
+ test_that {
41
+ Given { random_seeded_for_negative_float }
42
+ When { @int = any_int :positive }
43
+ Then { assert @int > 0,"We specified :positive, but got a negative" }
44
+ }
45
+
46
+ test_that {
47
+ Given { random_seeded_for_positive }
48
+ When { @int = any_int :negative }
49
+ Then { assert @int < 0,"We specified :negative, but got a positive" }
50
+ }
51
+
52
+ test_that {
53
+ Given { random_seeded_for_long_string }
54
+ When { @string = any_string }
55
+ Then {
56
+ assert @string.length < 1441,"expected less than our rand value, but got #{@string.length}"
57
+ assert_equal String,@string.class
58
+ }
59
+ }
60
+
61
+ test_that {
62
+ Given { random_seeded_for_long_string }
63
+ When { @string = any_string :max => 255 }
64
+ Then {
65
+ assert @string.length <= 255,"Expected a string of less than 256 characters, got #{@string.length}"
66
+ assert_equal String,@string.class
67
+ }
68
+ }
69
+
70
+ test_that {
71
+ Given { random_seeded_for_long_string }
72
+ When { @string = any_string :min => 1000 }
73
+ Then {
74
+ assert @string.length > 1000,"Expected a string of at least 1500 characters, got one of #{@string.length} characters"
75
+ assert_equal String,@string.class
76
+ }
77
+ }
78
+
79
+ test_that "we can register custom anys" do
80
+ Given {
81
+ new_any :foo do
82
+ "bar"
83
+ end
84
+ }
85
+ When {
86
+ @result = any :foo
87
+ }
88
+ Then {
89
+ assert_equal 'bar',@result
90
+ }
91
+ end
92
+
93
+ test_that "custom anys can take options" do
94
+ Given {
95
+ new_any :foo do |options|
96
+ if options[:baaz]
97
+ 'quux'
98
+ else
99
+ 'bar'
100
+ end
101
+ end
102
+ }
103
+ When {
104
+ @result = any :foo, :baaz => true
105
+ }
106
+ Then {
107
+ assert_equal 'quux',@result
108
+ }
109
+ end
110
+
111
+ [String,[Integer,Fixnum],Fixnum,Float,[Numeric,Float]].each do |klass|
112
+ test_that "can get an any #{klass}" do
113
+ Given {
114
+ @class = @expected_class = klass
115
+ if @class.kind_of? Array
116
+ @expected_class = @class[1]
117
+ @class = @class[0]
118
+ end
119
+ }
120
+ When {
121
+ @value = any @class
122
+ }
123
+ Then {
124
+ assert_not_nil @value
125
+ assert_equal @expected_class,@value.class
126
+ }
127
+ end
128
+ end
129
+
130
+ private
131
+
132
+ def random_seeded_for_long_string
133
+ Random.srand(34)
134
+ end
135
+
136
+ def random_seeded_for_negative_float
137
+ Random.srand(45)
138
+ end
139
+
140
+ def random_seeded_for_positive
141
+ Random.srand(2)
142
+ end
143
+
144
+ end
@@ -0,0 +1,17 @@
1
+ require 'clean_test/test_case'
2
+
3
+ class Circle
4
+ def initialize(radius)
5
+ @radius = radius
6
+ end
7
+
8
+ def area; (3.14 * @radius * @radius).to_i; end
9
+ end
10
+
11
+ class TestCircle < Clean::Test::TestCase
12
+ test_that {
13
+ Given { @circle = Circle.new(10) }
14
+ When { @area = @circle.area }
15
+ Then { assert_equal 314,@area }
16
+ }
17
+ end