quickie 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,6 @@
1
+ 0.3.0
2
+ - Implemented method stubs.
3
+
1
4
  0.2.0
2
5
  - Improved actual vs. expected reporting.
3
6
  - Improved user experience when running within IRB or Pry.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- quickie (0.2.0)
4
+ quickie (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  ## Quickie ##
2
- Quickie is micro library for quick in-place testing of your Ruby code. It adds two useful
3
- methods: <code>Object#should</code> and <code>Object#should\_not</code> for positive and
4
- negative assertions. With Quickie you can conveniently bundle tests together with your
5
- Ruby code, typically within <code>if $0 == \_\_FILE\_\_</code> conditional statement.
2
+ Quickie is micro library for quick in-place testing of your Ruby code. It adds three
3
+ useful methods: <code>Object#should</code> and <code>Object#should\_not</code> for
4
+ positive and negative assertions, and <code>Object#stub</code> for method stubbing.
5
+
6
+ With Quickie you can conveniently bundle tests along with your Ruby code, typically
7
+ within <code>if $0 == \_\_FILE\_\_</code> conditional statement.
6
8
 
7
9
  ### System Requirements ###
8
10
  Ruby 1.9.2 or later.
@@ -14,24 +16,18 @@ Ruby 1.9.2 or later.
14
16
  # Cloning the repository
15
17
  $ git clone git://github.com/michaeldv/quickie.git
16
18
 
17
- ### Usage Example ###
19
+ ### Usage Example - Assertions ###
18
20
 
19
- $ cat > sample.rb
21
+ $ cat > 1.rb
20
22
  class Account # Back account class.
21
23
  attr_reader :balance # Current account balance.
22
24
 
23
- def initialize(amount) # Open the account.
24
- @balance = amount # Accept initial deposit.
25
- end
26
-
27
- def deposit(amount) # Accept account deposit.
28
- @balance += amount # Update current balance.
25
+ def initialize(amount = 0) # Open the account.
26
+ @balance = amount.abs # Accept initial deposit.
29
27
  end
30
28
 
31
- def withdraw(amount) # Withdraw from the account.
32
- cash = [ @balance, amount ].min # Can't withdraw more than the balance.
33
- @balance -= cash # Update current balance.
34
- cash
29
+ def deposit(amount) # Accept a deposit.
30
+ @balance += amount.abs # Update current balance.
35
31
  end
36
32
 
37
33
  def status # Display account status.
@@ -46,30 +42,72 @@ Ruby 1.9.2 or later.
46
42
  acc.balance.should == 100 # Initial balance should be $100.
47
43
  acc.deposit(200) # Deposit $200 more.
48
44
  acc.balance.should != 100 # The balance should get updated.
49
- acc.balance.should == 300 # It should be $100 + $200 = $300.
45
+ acc.balance.should == 300 # $100 + $200 = $300.
50
46
 
51
47
  String.should === acc.status # Account#status returns a string.
52
48
  acc.status.should_not =~ /\$$/ # Status string should contain the balance.
53
- acc.status.should =~ /\$\d+$/ # The balance is one or more digits.
54
-
55
- acc.withdraw(500).should == 300 # Withdrawal that exeeds the balance is not allowed.
56
- acc.balance.should == 0 # Current balance should drop to zero.
57
- acc.status.should !~ /\$[1-9]+$/ # Status no longer shows positive number.
58
- acc.status.should =~ /\$0$/ # It shows $0.
49
+ acc.status.should =~ /\$\d+\.*\d*$/ # Balance contains digits with optional separator.
59
50
  end
60
51
  ^D
61
- $ ruby sample.rb
62
- ..........
52
+ $ ruby 1.rb
53
+ ......
54
+
55
+ Passed: 6, not quite: 0, total tests: 6.
56
+
57
+ ### Usage Example - Method Stubs ###
58
+ To set up a stub with optional return value use <code>obj.stub(:method, :return => value)</code>.
59
+ To remove existing stub and restore original method use <code>obj.stub(:method, :remove)</code>.
60
+
61
+ $ cat > 2.rb
62
+ require "net/http"
63
+ require "json"
64
+ require "uri"
65
+
66
+ class GemStats # Get gems stats from rubygems.org.
67
+ attr_reader :downloads
68
+
69
+ def initialize(gem, version)
70
+ uri = URI.parse("http://rubygems.org/api/v1/downloads/#{gem}-#{version}.json")
71
+ response = Net::HTTP.get_response(uri)
72
+ @downloads = JSON.parse(response.body)
73
+ end
74
+
75
+ def total
76
+ @downloads["total_downloads"]
77
+ end
78
+
79
+ def version
80
+ @downloads["version_downloads"]
81
+ end
82
+ end
63
83
 
64
- Passed: 10, not quite: 0, total tests: 10.
84
+ if $0 == __FILE__
85
+ require "quickie"
86
+
87
+ response = { :total_downloads => 999_999, :version_downloads => 999 }.to_json
88
+ response.stub(:body, :return => response)
89
+ Net::HTTP.stub(:get_response, :return => response)
90
+
91
+ stats = GemStats.new(:awesome_print, '1.0.2')
92
+
93
+ Hash.should === stats.downloads # Downloads should ba a hash.
94
+ stats.downloads.keys.size.should == 2 # It should have two keys.
95
+ stats.total.should == 999_999 # Total downloads should match test data.
96
+ stats.version.should == 999 # Ditto for version.
97
+ end
98
+ ^D
99
+ $ ruby 2.rb
100
+ ....
101
+
102
+ Passed: 4, not quite: 0, total tests: 4.
65
103
 
66
104
  ### Testing Quickie ###
67
105
  Quickie code is tested by the Quickie itself.
68
106
 
69
107
  $ ruby test/quickie_test.rb
70
- ....................
108
+ ................................
71
109
 
72
- Passed: 20, not quite: 0, total tests: 20.
110
+ Passed: 32, not quite: 0, total tests: 32.
73
111
 
74
112
  ### Note on Patches/Pull Requests ###
75
113
  * Fork the project on Github.
@@ -11,5 +11,6 @@ abort "Quickie requires Ruby 1.9.2 or later" if RUBY_VERSION < "1.9.2"
11
11
 
12
12
  require File.dirname(__FILE__) + "/quickie/runner"
13
13
  require File.dirname(__FILE__) + "/quickie/matcher"
14
+ require File.dirname(__FILE__) + "/quickie/stub"
14
15
  require File.dirname(__FILE__) + "/quickie/version"
15
16
  require File.dirname(__FILE__) + "/quickie/core_ext/object"
@@ -10,4 +10,9 @@ class Object
10
10
  end
11
11
  alias_method :"#{verb}_be", verb
12
12
  end
13
+
14
+ define_method :stub do |method, options = {}|
15
+ Quickie::Stub.new(self, method, options)
16
+ end
17
+ alias_method :stub!, :stub
13
18
  end
@@ -0,0 +1,109 @@
1
+ # Copyright (c) 2011-12 Michael Dvorkin
2
+ #
3
+ # Quickie is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ module Quickie
7
+ class Stub
8
+ #
9
+ # To set up a stub with optional return value:
10
+ # obj.stub(:method, :return => something)
11
+ #
12
+ # To remove existing stub and restore original method:
13
+ # obj.stub(:method, :remove)
14
+ #
15
+ #--------------------------------------------------------------------------
16
+ def initialize(object, method, options = {})
17
+ options = { options => true } if options.is_a?(Symbol)
18
+ @object, @options = object, options
19
+ @@stash ||= []
20
+ #
21
+ # Create a new stub by intercepting the method or remove existing stub
22
+ # by restoring the original method.
23
+ #
24
+ unless @options[:remove]
25
+ intercept(method)
26
+ else
27
+ restore(method)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ # Same as class << @object; self; end -- comes with Ruby 1.9.
34
+ #--------------------------------------------------------------------------
35
+ def metaclass
36
+ @object.singleton_class
37
+ end
38
+
39
+ # Unique name the original method gets stashed under when creating a stub.
40
+ #--------------------------------------------------------------------------
41
+ def moniker(method)
42
+ :"__#{method}__#{@object.__id__}"
43
+ end
44
+
45
+ # Return method's visibility, nil if public.
46
+ #--------------------------------------------------------------------------
47
+ def visibility(method)
48
+ if metaclass.private_method_defined?(method)
49
+ :private
50
+ elsif metaclass.protected_method_defined?(method)
51
+ :protected
52
+ end
53
+ end
54
+
55
+ # Set up a stub by stashing the original method under different name and
56
+ # then rediefining the method to return the requested value.
57
+ #--------------------------------------------------------------------------
58
+ def intercept(method)
59
+ new_name = moniker(method)
60
+ unless @object.respond_to? new_name
61
+ stash(method, new_name)
62
+ redefine(method)
63
+ end
64
+ end
65
+
66
+ # Preserve original method by creating its alias with the unique name.
67
+ #--------------------------------------------------------------------------
68
+ def stash(method, new_name)
69
+ metaclass.class_eval do
70
+ if method_defined?(method) || private_method_defined?(method)
71
+ alias_method new_name, method
72
+ end
73
+ end
74
+ @@stash << new_name
75
+ end
76
+
77
+ # Create a stub that returns requested value.
78
+ #--------------------------------------------------------------------------
79
+ def redefine(method)
80
+ return_value = @options[:return]
81
+ metaclass.class_eval do
82
+ define_method method do |*args, &block|
83
+ return_value
84
+ end
85
+ end
86
+ #
87
+ # Set visibility attribute if the origial method is not public.
88
+ #
89
+ private_or_protected = visibility(method)
90
+ metaclass.class_eval("#{private_or_protected} :#{method}") if private_or_protected
91
+ end
92
+
93
+ # Remove the stub and restore the original method.
94
+ #--------------------------------------------------------------------------
95
+ def restore(method)
96
+ stashed_name = moniker(method)
97
+ if @@stash.include? stashed_name # Was it ever stubbed?
98
+ metaclass.instance_eval do
99
+ if method_defined?(stashed_name) || private_method_defined?(stashed_name)
100
+ remove_method method # Remove the stub.
101
+ alias_method method, stashed_name # Restore the original method from stash.
102
+ remove_method stashed_name # Remove stashed copy.
103
+ end
104
+ end
105
+ @@stash.delete stashed_name
106
+ end
107
+ end
108
+ end
109
+ end
@@ -5,6 +5,6 @@
5
5
  #------------------------------------------------------------------------------
6
6
  module Quickie
7
7
  def self.version
8
- '0.2.0'
8
+ '0.3.0'
9
9
  end
10
10
  end
@@ -0,0 +1,65 @@
1
+ # Copyright (c) 2011-12 Michael Dvorkin
2
+ #
3
+ # Quickie is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ require "stringio"
7
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/quickie")
8
+
9
+ # Use Quickie to test itself. The methodology is as follows:
10
+ #
11
+ # 1. Write regular Quickie test.
12
+ # 2. Capture the output of the test.
13
+ # 3. Make sure captured output matches the expectation.
14
+ #
15
+ # In addition, we hack the Quickie trace/stats so that failed captured
16
+ # tests are not shown/counted in the actual results.
17
+ #--------------------------------------------------------------------------
18
+ def capture
19
+ stats = Quickie::Runner.class_variable_get(:@@stats)
20
+ trace = Quickie::Runner.class_variable_get(:@@trace)
21
+
22
+ standard, $stdout = $stdout, StringIO.new
23
+ yield
24
+ $stdout.string
25
+ ensure
26
+ if $stdout.string == '.'
27
+ stats[:success] -= 1
28
+ else
29
+ stats[:failure] -= 1
30
+ trace.pop
31
+ end
32
+ $stdout = standard
33
+ end
34
+
35
+ # Should - passing specs.
36
+ #--------------------------------------------------------------------------
37
+ capture { "abc".should == "abc" }.should == "."
38
+ capture { "abc".should != "xyz" }.should == "."
39
+ capture { "abc".should =~ /AB/i }.should == "."
40
+ capture { "abc".should !~ /XY/i }.should == "."
41
+ capture { 1234567.should_be > 0 }.should == "."
42
+
43
+ # Should Not - passing specs.
44
+ #--------------------------------------------------------------------------
45
+ capture { "abc".should_not != "abc" }.should == "."
46
+ capture { "abc".should_not == "xyz" }.should == "."
47
+ capture { "abc".should_not !~ /AB/i }.should == "."
48
+ capture { "abc".should_not =~ /XY/i }.should == "."
49
+ capture { 1234567.should_not_be < 0 }.should == "."
50
+
51
+ # Should - failing specs.
52
+ #--------------------------------------------------------------------------
53
+ capture { "abc".should != "abc" }.should == "F"
54
+ capture { "abc".should == "xyz" }.should == "F"
55
+ capture { "abc".should !~ /AB/i }.should == "F"
56
+ capture { "abc".should =~ /XY/i }.should == "F"
57
+ capture { 1234567.should_be < 0 }.should == "F"
58
+
59
+ # Should Not - failing specs.
60
+ #--------------------------------------------------------------------------
61
+ capture { "abc".should_not == "abc" }.should == "F"
62
+ capture { "abc".should_not != "xyz" }.should == "F"
63
+ capture { "abc".should_not =~ /AB/i }.should == "F"
64
+ capture { "abc".should_not !~ /XY/i }.should == "F"
65
+ capture { 1234567.should_not_be > 0 }.should == "F"
@@ -3,63 +3,5 @@
3
3
  # Quickie is freely distributable under the terms of MIT license.
4
4
  # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
5
  #------------------------------------------------------------------------------
6
- require "stringio"
7
- require File.expand_path(File.dirname(__FILE__) + "/../lib/quickie")
8
-
9
- # Use Quickie to test itself. The methodology is as follows:
10
- #
11
- # 1. Write regular Quickie test.
12
- # 2. Capture the output of the test.
13
- # 3. Make sure captured output matches the expectation.
14
- #
15
- # In addition, we hack the Quickie trace/stats so that failed captured
16
- # tests are not shown/counted in the actual results.
17
- #--------------------------------------------------------------------------
18
- def capture
19
- stats = Quickie::Runner.class_variable_get(:@@stats)
20
- trace = Quickie::Runner.class_variable_get(:@@trace)
21
-
22
- standard, $stdout = $stdout, StringIO.new
23
- yield
24
- $stdout.string
25
- ensure
26
- if $stdout.string == '.'
27
- stats[:success] -= 1
28
- else
29
- stats[:failure] -= 1
30
- trace.pop
31
- end
32
- $stdout = standard
33
- end
34
-
35
- # Should - passing specs.
36
- #--------------------------------------------------------------------------
37
- capture { "abc".should == "abc" }.should == "."
38
- capture { "abc".should != "xyz" }.should == "."
39
- capture { "abc".should =~ /AB/i }.should == "."
40
- capture { "abc".should !~ /XY/i }.should == "."
41
- capture { 1234567.should_be > 0 }.should == "."
42
-
43
- # Should Not - passing specs.
44
- #--------------------------------------------------------------------------
45
- capture { "abc".should_not != "abc" }.should == "."
46
- capture { "abc".should_not == "xyz" }.should == "."
47
- capture { "abc".should_not !~ /AB/i }.should == "."
48
- capture { "abc".should_not =~ /XY/i }.should == "."
49
- capture { 1234567.should_not_be < 0 }.should == "."
50
-
51
- # Should - failing specs.
52
- #--------------------------------------------------------------------------
53
- capture { "abc".should != "abc" }.should == "F"
54
- capture { "abc".should == "xyz" }.should == "F"
55
- capture { "abc".should !~ /AB/i }.should == "F"
56
- capture { "abc".should =~ /XY/i }.should == "F"
57
- capture { 1234567.should_be < 0 }.should == "F"
58
-
59
- # Should Not - failing specs.
60
- #--------------------------------------------------------------------------
61
- capture { "abc".should_not == "abc" }.should == "F"
62
- capture { "abc".should_not != "xyz" }.should == "F"
63
- capture { "abc".should_not =~ /AB/i }.should == "F"
64
- capture { "abc".should_not !~ /XY/i }.should == "F"
65
- capture { 1234567.should_not_be > 0 }.should == "F"
6
+ require File.expand_path(File.dirname(__FILE__) + "/matcher_test")
7
+ require File.expand_path(File.dirname(__FILE__) + "/stub_test")
@@ -0,0 +1,29 @@
1
+ # Copyright (c) 2011-12 Michael Dvorkin
2
+ #
3
+ # Quickie is freely distributable under the terms of MIT license.
4
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/quickie")
7
+
8
+ numbers = [ 1, 2, 3 ]
9
+ letters = %w(a b c)
10
+
11
+ numbers.stub! :join, :return => 42 # Stub numbers#join to return arbitrary value.
12
+ numbers.join.should == 42 # Test numbers.join().
13
+ numbers.join(",").should == 42 # Test numbers.join(arg).
14
+ letters.join.should == "abc" # letters array is unaffected by numbers#join.
15
+
16
+ letters.stub! :join, :return => "Hello, world!" # Now stub letters#join.
17
+ letters.join.should == "Hello, world!" # Test letters.join().
18
+ letters.join(",").should == "Hello, world!" # Test letters.join(arg).
19
+ numbers.join.should == 42 # numbers#join stub is unaffected by letters#join stub.
20
+ numbers.join(",").should == 42 # Ditto.
21
+
22
+ numbers.stub :join, :remove # Remove numbers#join stub.
23
+ numbers.join.should == "123" # numbers.join() should work as expected.
24
+ numbers.join(",").should == "1,2,3" # numbers.join(arg) should work as expected.
25
+ letters.join.should == "Hello, world!" # letters#join remains stubbed.
26
+
27
+ letters.stub :join, :remove # Now remove letters#join stub.
28
+ letters.join.should == "abc" # letters.join() should work as expected.
29
+ letters.join(",").should == "a,b,c" # letters.join(arg) should work as expected.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quickie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,8 +11,8 @@ bindir: bin
11
11
  cert_chain: []
12
12
  date: 2012-01-06 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: Quickie adds Object#should and Object#should_not methods for quick testing
15
- of your Ruby code
14
+ description: Quickie adds Object#should, Object#should_not, and Object#stub methods
15
+ for quick and easy testing of your Ruby code
16
16
  email: mike@dvorkin.net
17
17
  executables: []
18
18
  extensions: []
@@ -27,9 +27,12 @@ files:
27
27
  - lib/quickie/core_ext/object.rb
28
28
  - lib/quickie/matcher.rb
29
29
  - lib/quickie/runner.rb
30
+ - lib/quickie/stub.rb
30
31
  - lib/quickie/version.rb
31
32
  - lib/quickie.rb
33
+ - test/matcher_test.rb
32
34
  - test/quickie_test.rb
35
+ - test/stub_test.rb
33
36
  - .gitignore
34
37
  homepage: http://github.com/michaeldv/quickie
35
38
  licenses: []
@@ -55,4 +58,7 @@ rubygems_version: 1.8.11
55
58
  signing_key:
56
59
  specification_version: 3
57
60
  summary: Micro framework for in-place testing of Ruby code
58
- test_files: []
61
+ test_files:
62
+ - test/matcher_test.rb
63
+ - test/quickie_test.rb
64
+ - test/stub_test.rb