quickie 0.2.0 → 0.3.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.
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