test_unit-given 0.9.3 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,16 +6,31 @@ License:: Distributes under the Apache License, see LICENSE.txt in the source di
6
6
 
7
7
  Get your Test::Unit test cases fluent, without RSpec, magic, or crazy meta-programming.
8
8
 
9
- This gives you two simple tools to make your test cases readable:
9
+ This gives you some simple tools to make your test cases readable:
10
10
 
11
11
  * Given/When/Then to delineate which parts of your tests do what
12
12
  * A method "test_that" that defines test cases with strings instead of method names (much like +test+ in Rails tests)
13
+ * A module Any that defines methods to create arbitrary values, so it's clear in your tests what values are what, but without having to use long method names like `Faker::Lorem.words(2).join(' ')`
13
14
 
14
15
  == Install
15
16
 
16
17
  gem install test_unit-given
17
18
 
18
- == Example
19
+ == `test_that`
20
+
21
+ class SomeTest < Test::Unit::Given::TestCase
22
+ test_that {
23
+ # do a test
24
+ }
25
+
26
+ test_that "description of our test" do
27
+ # do a test
28
+ end
29
+ end
30
+
31
+ Yes, just like `test` from Rails, but without having to have ActiveSupport
32
+
33
+ == Given/When/Then
19
34
 
20
35
  class Circle
21
36
  def initialize(radius)
@@ -212,6 +227,67 @@ Again, things are a bit out of order, but if you invert Then and When, you'll st
212
227
  end
213
228
  end
214
229
 
230
+ == Any
231
+
232
+ Our tests tend to have a lot of arbitrary or meaningless values. They also have a lot of very important and meaningfule values. Often both of these are expressed as literals in our code. What the Any module gives you is the ability to hide arbitrary values behind a method call. This will ensure that the values truly are arbitrary, and will also mean that any literals left in your tests are important. For example, you might have a test like this:
233
+
234
+
235
+ def test_something
236
+ service = mock()
237
+ service.expects(:remote_call).with({ :method => 'process', :amount => 45.6}).returns(87)
238
+ object_under_test = ObjectUnderTest.new('foo',service)
239
+
240
+ assert_equal 8700,object_under_test.doit
241
+ end
242
+
243
+ What's being tested here? What values are meaningfule and which aren't? Let's apply Any to make it clear:
244
+
245
+ def test_something
246
+ service = mock()
247
+ service_return = any_int
248
+ service.expects(:remote_call).with({ :method => any_string, :amount => any_number}).returns(service_return)
249
+ object_under_test = ObjectUnderTest.new(any_string,service)
250
+
251
+ assert_equal (service_return * 100),object_under_test.doit
252
+ end
253
+
254
+ *Now* it's clear that we're expecting our object under test to multiple the return of our service call by 100. The only value that has any meaning to this test is the integer 100, and that's the only literal that's there. Beauty.
255
+
256
+ === What about Faker or Sham?
257
+
258
+ They simply don't provide a concise API to do this, nor do they really communicate this concept. We aren't passing _fake_ strings or numbers, we're passing *arbitrary* strings and numbers. It's worth making that clear in our tests that certainly values that must not be nil, don't matter to the test. That they are random each time keeps us honest.
259
+
260
+ === What if I need some sort of restriction?
261
+
262
+ Two ways to do that. The built-in any_* methods provide limited options:
263
+
264
+ def test_truncate
265
+ person = Person.new
266
+ person.name = any_string :min => 256
267
+ person.age = any_int :positive
268
+ person.save!
269
+ assert_equal 255,person.name.length
270
+ end
271
+
272
+ The second way is to create your own any:
273
+
274
+ def setup
275
+ new_any :age do |options|
276
+ age = any_number % 80
277
+ age += 18 if options == :adult
278
+ age
279
+ end
280
+ end
281
+
282
+ def test_truncate
283
+ person = Person.new
284
+ person.name = any_string :min => 256
285
+ person.age = any :age, :adult
286
+ person.save!
287
+ assert_equal 255,person.name.length
288
+ end
289
+
290
+
215
291
  == WTF? Why?
216
292
 
217
293
  Just because you're using Test::Unit doesn't mean you can't write fluent, easy to understand tests.
data/Rakefile CHANGED
@@ -21,7 +21,7 @@ RDoc::Task.new do |rd|
21
21
  rd.main = "README.rdoc"
22
22
  rd.generator = 'hanna'
23
23
  rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
24
- rd.title = 'Methadone - Power Up your Command Line Apps'
24
+ rd.title = 'Test::Unit::Given - make your unit tests clear'
25
25
  rd.markup = "tomdoc"
26
26
  end
27
27
  CLOBBER << 'coverage'
@@ -0,0 +1,137 @@
1
+ require 'faker'
2
+
3
+ module Test
4
+ module Unit
5
+ module Given
6
+ # Public: Provides the ability to vend arbitrary values without using literals, or
7
+ # long calls to Faker. This has two levels of utility:
8
+ #
9
+ # helper methods - #any_number, #any_int, #any_string provide arbitrary primitives
10
+ # to make it clear what numbers, ints, and strings in your tests are
11
+ # relevant. Arbitrary values should use one of these any_ helpers.
12
+ #
13
+ # any sort of any - you can define your own "any" values by using #new_any, which allows you to
14
+ # extend things if you like. Of course, you could just make your own any_method as well.
15
+ module Any
16
+ MAX_RAND = 50000
17
+
18
+ # Public: Get any number; one that doesn't matter
19
+ #
20
+ # options - options to control what sort of number comes back:
21
+ # :positive - make sure that the number is greater than zero
22
+ # :negative - make sure that the number is less than zero
23
+ def any_number(*options)
24
+ any :number,options
25
+ end
26
+
27
+ # Public: Returns an integer. options is the same as for #any_number
28
+ def any_int(*options)
29
+ any :int,options
30
+ end
31
+
32
+ # Public: Get an arbitrary string of any potential length (the real max is 2048 characters if you don't override it)
33
+ #
34
+ # options - options to control the returned string
35
+ # :max the max size of the string you want
36
+ # :min the minimum size we want to come back
37
+ #
38
+ # Example
39
+ #
40
+ # any_string :max => 255 # => ensure it'll fit into a varchar(255)
41
+ # any_string :min => 1024 # at least 1024 characters
42
+ #
43
+ def any_string(options = {})
44
+ any :string,options
45
+ end
46
+
47
+ # Public: Get a predefined, arbitrary any.
48
+ #
49
+ # sym - the any that has been defined already. By default, the following are defined:
50
+ # :string - does any_string
51
+ # String - does any_string
52
+ # :number - does any_number
53
+ # Numeric - does any_number
54
+ # Float - does any_number
55
+ # :int - does any_int
56
+ # Fixnum - does any_int
57
+ # Integer - does any_int
58
+ # options - whatever options are relevant to the user-defined any
59
+ #
60
+ # Example
61
+ #
62
+ # new_any(:foo) do |options|
63
+ # if options[:bar]
64
+ # 'bar'
65
+ # else
66
+ # 'quux'
67
+ # end
68
+ # end
69
+ #
70
+ # some_foo = any :foo
71
+ # some_other_foo = any :foo, :bar => true
72
+ def any(sym,options = {})
73
+ anies[sym].call(options)
74
+ end
75
+
76
+ # Public: Create a new any that can be retrieved via #any
77
+ #
78
+ # any - the identifer of your new any. Can be anything though a Symbol or classname would be appropriate
79
+ # block - the block that will be called whenever you #any your any.
80
+ def new_any(any,&block)
81
+ anies[any] = block
82
+ end
83
+
84
+ private
85
+
86
+ def anies
87
+ @anies ||= default_anies
88
+ end
89
+
90
+ ANY_STRING = Proc.new do |options|
91
+ max_size = options[:max] || rand(2048)
92
+ min_size = options[:min] || rand(1024)
93
+ min_size = max_size if min_size > max_size
94
+
95
+ size = rand(max_size)
96
+
97
+ size = min_size if size < min_size
98
+
99
+ string = Faker::Lorem.words(1).join('')
100
+ while string.length < size
101
+ string += Faker::Lorem.words(1).join('')
102
+ end
103
+
104
+ string[0..(max_size-1)]
105
+ end
106
+
107
+ ANY_NUMBER = Proc.new do |options|
108
+ number = (rand(2 * MAX_RAND) - MAX_RAND).to_f/100.0
109
+ if options.include? :positive
110
+ number + MAX_RAND
111
+ elsif options.include? :negative
112
+ number - MAX_RAND
113
+ else
114
+ number
115
+ end
116
+ end
117
+
118
+ ANY_INT = Proc.new do |options|
119
+ (ANY_NUMBER.call(options)).to_i
120
+ end
121
+
122
+ def default_anies
123
+ { :string => ANY_STRING,
124
+ String => ANY_STRING,
125
+ :number => ANY_NUMBER,
126
+ Numeric => ANY_NUMBER,
127
+ Float => ANY_NUMBER,
128
+ :int => ANY_INT,
129
+ Fixnum => ANY_INT,
130
+ Integer => ANY_INT,
131
+ }
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+
@@ -1,5 +1,6 @@
1
1
  require 'test/unit'
2
2
  require 'test/unit/given/simple'
3
+ require 'test/unit/given/any'
3
4
  require 'test/unit/given/test_that'
4
5
 
5
6
  module Test
@@ -21,6 +22,7 @@ module Test
21
22
  class TestCase < Test::Unit::TestCase
22
23
  include Simple
23
24
  include TestThat
25
+ include Any
24
26
  if RUBY_VERSION =~ /^1\.8\./
25
27
  # Avoid the stupid behavior of
26
28
  # complaining that no tests were specified for 1.8.-like rubies
@@ -1,7 +1,7 @@
1
1
  module Test
2
2
  module Unit
3
3
  module Given
4
- VERSION = "0.9.3"
4
+ VERSION = "0.9.4"
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,144 @@
1
+ require 'test/unit'
2
+ require 'test/unit/given'
3
+
4
+ class TestAny < Test::Unit::Given::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
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
+ s.add_dependency("faker")
21
22
  s.add_development_dependency("rdoc")
22
23
  s.add_development_dependency("rake")
23
24
  s.add_development_dependency("simplecov")
metadata CHANGED
@@ -1,105 +1,113 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: test_unit-given
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.4
4
5
  prerelease:
5
- version: 0.9.3
6
6
  platform: ruby
7
- authors:
8
- - David Copeland
7
+ authors:
8
+ - David Copeland
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-11-21 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: rdoc
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
19
- none: false
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "0"
24
- type: :development
25
- version_requirements: *id001
26
- - !ruby/object:Gem::Dependency
27
- name: rake
28
- prerelease: false
29
- requirement: &id002 !ruby/object:Gem::Requirement
30
- none: false
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: "0"
35
- type: :development
36
- version_requirements: *id002
37
- - !ruby/object:Gem::Dependency
38
- name: simplecov
39
- prerelease: false
40
- requirement: &id003 !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ">="
44
- - !ruby/object:Gem::Version
45
- version: "0"
46
- type: :development
47
- version_requirements: *id003
48
- description: We don't need no stinkin' RSpec! Get all the fluency you want in your Test::Unit tests, with no magic required, using straight Ruby syntax
49
- email:
50
- - davetron5000@gmail.com
12
+ date: 2011-11-23 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: faker
16
+ requirement: &70233017866940 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70233017866940
25
+ - !ruby/object:Gem::Dependency
26
+ name: rdoc
27
+ requirement: &70233017866520 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70233017866520
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70233017866100 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70233017866100
47
+ - !ruby/object:Gem::Dependency
48
+ name: simplecov
49
+ requirement: &70233000056260 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70233000056260
58
+ description: We don't need no stinkin' RSpec! Get all the fluency you want in your
59
+ Test::Unit tests, with no magic required, using straight Ruby syntax
60
+ email:
61
+ - davetron5000@gmail.com
51
62
  executables: []
52
-
53
63
  extensions: []
54
-
55
64
  extra_rdoc_files: []
56
-
57
- files:
58
- - .gitignore
59
- - .rvmrc
60
- - Gemfile
61
- - LICENSE.txt
62
- - README.rdoc
63
- - Rakefile
64
- - lib/test/unit/given.rb
65
- - lib/test/unit/given/simple.rb
66
- - lib/test/unit/given/test_case.rb
67
- - lib/test/unit/given/test_that.rb
68
- - lib/test/unit/given/version.rb
69
- - test/bootstrap.rb
70
- - test/test_circle.rb
71
- - test/test_simple_given.rb
72
- - test/test_test_that.rb
73
- - test_unit-given.gemspec
74
- homepage: ""
65
+ files:
66
+ - .gitignore
67
+ - .rvmrc
68
+ - Gemfile
69
+ - LICENSE.txt
70
+ - README.rdoc
71
+ - Rakefile
72
+ - lib/test/unit/given.rb
73
+ - lib/test/unit/given/any.rb
74
+ - lib/test/unit/given/simple.rb
75
+ - lib/test/unit/given/test_case.rb
76
+ - lib/test/unit/given/test_that.rb
77
+ - lib/test/unit/given/version.rb
78
+ - test/bootstrap.rb
79
+ - test/test_any.rb
80
+ - test/test_circle.rb
81
+ - test/test_simple_given.rb
82
+ - test/test_test_that.rb
83
+ - test_unit-given.gemspec
84
+ homepage: ''
75
85
  licenses: []
76
-
77
86
  post_install_message:
78
87
  rdoc_options: []
79
-
80
- require_paths:
81
- - lib
82
- required_ruby_version: !ruby/object:Gem::Requirement
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
83
91
  none: false
84
- requirements:
85
- - - ">="
86
- - !ruby/object:Gem::Version
87
- version: "0"
88
- required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
97
  none: false
90
- requirements:
91
- - - ">="
92
- - !ruby/object:Gem::Version
93
- version: "0"
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
94
102
  requirements: []
95
-
96
103
  rubyforge_project: test_unit-given
97
- rubygems_version: 1.8.9
104
+ rubygems_version: 1.8.10
98
105
  signing_key:
99
106
  specification_version: 3
100
107
  summary: Use Given/When/Then in your Test::Unit tests
101
- test_files:
102
- - test/bootstrap.rb
103
- - test/test_circle.rb
104
- - test/test_simple_given.rb
105
- - test/test_test_that.rb
108
+ test_files:
109
+ - test/bootstrap.rb
110
+ - test/test_any.rb
111
+ - test/test_circle.rb
112
+ - test/test_simple_given.rb
113
+ - test/test_test_that.rb