cross-stub 0.1.4 → 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 (39) hide show
  1. data/HISTORY.txt +12 -0
  2. data/README.rdoc +85 -29
  3. data/Rakefile +8 -1
  4. data/VERSION +1 -1
  5. data/cross-stub.gemspec +37 -19
  6. data/lib/cross-stub.rb +63 -24
  7. data/lib/cross-stub/arguments.rb +21 -0
  8. data/lib/cross-stub/arguments/array.rb +16 -0
  9. data/lib/cross-stub/arguments/hash.rb +17 -0
  10. data/lib/cross-stub/arguments/proc.rb +73 -0
  11. data/lib/cross-stub/cache.rb +36 -0
  12. data/lib/cross-stub/stores.rb +4 -0
  13. data/lib/cross-stub/stores/base.rb +38 -0
  14. data/lib/cross-stub/stores/file.rb +39 -0
  15. data/lib/cross-stub/stores/memcache.rb +40 -0
  16. data/lib/cross-stub/stores/redis.rb +41 -0
  17. data/lib/cross-stub/stubber.rb +132 -0
  18. data/rails_generators/cross_stub/cross_stub_generator.rb +1 -1
  19. data/rails_generators/cross_stub/templates/config/initializers/cross-stub.rb +9 -1
  20. data/rails_generators/cross_stub/templates/features/support/cross-stub.rb +16 -2
  21. data/spec/arguments/proc_spec.rb +689 -0
  22. data/spec/includes.rb +103 -0
  23. data/spec/integration/clearing_instance_stubs_spec.rb +119 -0
  24. data/spec/integration/clearing_stubs_spec.rb +118 -0
  25. data/spec/integration/creating_instance_stubs_spec.rb +91 -0
  26. data/spec/integration/creating_stubs_spec.rb +95 -0
  27. data/spec/integration/shared_spec.rb +35 -0
  28. data/spec/integration/stubbing_error_spec.rb +69 -0
  29. data/spec/service.rb +114 -0
  30. data/spec/spec_helper.rb +1 -41
  31. metadata +58 -26
  32. data/lib/cross-stub/cache_helpers.rb +0 -48
  33. data/lib/cross-stub/pseudo_class.rb +0 -82
  34. data/lib/cross-stub/setup_helpers.rb +0 -13
  35. data/lib/cross-stub/stub_helpers.rb +0 -64
  36. data/spec/cross-stub/clearing_stubs_spec.rb +0 -112
  37. data/spec/cross-stub/creating_stubs_spec.rb +0 -110
  38. data/spec/cross-stub/stubbing_error_spec.rb +0 -38
  39. data/spec/helpers.rb +0 -125
data/spec/includes.rb ADDED
@@ -0,0 +1,103 @@
1
+ unless Object.const_defined?(:RUBY_VM)
2
+
3
+ # ///////////////////////////////////////////////////////////////////////
4
+ # Constants
5
+ # ///////////////////////////////////////////////////////////////////////
6
+
7
+ RUBY_VM = [
8
+ RUBY_VERSION.gsub(/[^\d]/,''),
9
+ RUBY_PLATFORM =~ /java/i ? 'j' : '',
10
+ RUBY_DESCRIPTION =~ /enterprise/i ? 'e' : ''
11
+ ].join
12
+
13
+ PROJECT_ROOT = File.join(File.dirname(__FILE__), '..')
14
+ PROJECT_FILE = lambda{|*args| File.join(*[PROJECT_ROOT, *args]) }
15
+
16
+ CACHE_STORES = {
17
+ :file => PROJECT_FILE['tmp', "stubbing-#{RUBY_VM}.cache"],
18
+ :memcache => "localhost:11211/stubbing-#{RUBY_VM}.cache",
19
+ :redis => "localhost:6379/stubbing-#{RUBY_VM}.cache",
20
+ }
21
+
22
+ ECHO_SERVER_INIT_WAIT_TIME = RUBY_VM.end_with?('j') ? 10 : 2
23
+ ECHO_SERVER_LOG = PROJECT_FILE['tmp', "echoserver-#{RUBY_VM}.log"]
24
+ ECHO_SERVER_HOST = '127.0.0.1'
25
+ ECHO_SERVER_PORT = 10000 + RUBY_VM[/(\d+)/,1].to_i + ({'j' => 7, 'e' => 17}[RUBY_VM[-1..-1]] || 0)
26
+
27
+ # /////////////////////////////////////////////////////////////////////////////////////////
28
+ # Useful methods
29
+ # /////////////////////////////////////////////////////////////////////////////////////////
30
+
31
+ def cache_store(id)
32
+ {(id = :"#{id}") => CACHE_STORES[id]}
33
+ end
34
+
35
+ def each_cache_store(&block)
36
+ CACHE_STORES.keys.each do |store_type|
37
+ yield(store_type)
38
+ end
39
+ end
40
+
41
+ def do_local_method_call(klass_and_method_and_args)
42
+ klass, is_instance, method, args = parse_call_args(klass_and_method_and_args)
43
+ receiver = is_instance ? klass.new : klass
44
+ args.empty? ? receiver.send(method) : receiver.send(method, *args)
45
+ end
46
+
47
+ def parse_call_args(klass_and_method_and_args)
48
+ klass_descriptor, method, *args = klass_and_method_and_args.split('.')
49
+ is_instance = klass_descriptor.end_with?(suffix = '#new')
50
+ [klassify(klass_descriptor), is_instance, method, args]
51
+ end
52
+
53
+ def do_remote_method_call(store_type_and_klass_and_method_and_args)
54
+ (value = EchoClient.get(store_type_and_klass_and_method_and_args)) !~ /^undefined method/ ?
55
+ value : Object.we_just_wanna_trigger_no_method_error_with_this_weird_method!
56
+ end
57
+
58
+ def klassify(descriptor)
59
+ descriptor.sub('#new','').split(/::/).inject(Object){|k,c| k.const_get(c) }
60
+ end
61
+
62
+ # /////////////////////////////////////////////////////////////////////////////////////////
63
+ # Loading of files
64
+ # /////////////////////////////////////////////////////////////////////////////////////////
65
+
66
+ $LOAD_PATH.unshift(PROJECT_FILE['lib'])
67
+ require 'cross-stub'
68
+
69
+ # /////////////////////////////////////////////////////////////////////////////////////////
70
+ # Class definitions for specs
71
+ # /////////////////////////////////////////////////////////////////////////////////////////
72
+
73
+ class AnyClass
74
+
75
+ def self.say ; 'hello' ; end
76
+
77
+ class Inner
78
+ def self.say ; 'hello' ; end
79
+ end
80
+
81
+ end
82
+
83
+ class AnyInstance
84
+
85
+ def say ; 'hello' ; end
86
+
87
+ class Inner
88
+ def say ; 'hello' ; end
89
+ end
90
+
91
+ end
92
+
93
+ module AnyModule
94
+
95
+ def self.say ; 'hello' ; end
96
+
97
+ module Inner
98
+ def self.say ; 'hello' ; end
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,119 @@
1
+ require File.join(File.dirname(__FILE__), 'shared_spec')
2
+
3
+ describe 'Clearing Instance Stubs' do
4
+
5
+ each_cache_store do |store_type|
6
+ %w{current other}.each do |mode|
7
+ %w{AnyInstance#new AnyInstance::Inner#new}.each do |descriptor|
8
+
9
+ describe '>> %s process using :%s store (%s instance)' % [mode, store_type, descriptor] do
10
+
11
+ before do
12
+ @descriptor = descriptor
13
+ @klass = klassify(descriptor)
14
+ @instance = @klass.new
15
+ @store_type = store_type
16
+ end
17
+
18
+ behaves_like "has #{mode} process setup"
19
+
20
+ should "clear hash generated stub and return original value" do
21
+ original_value = @instance.say
22
+ @klass.xstub({:say => 'HELLO'}, :instance => true)
23
+ CrossStub.clear
24
+ @call["#{@descriptor}.say"].should.equal original_value
25
+ end
26
+
27
+ should "clear hash generated stub and raise NoMethodError" do
28
+ should.raise(NoMethodError) do
29
+ @klass.xstub({:blurb => 'blah blah'}, :instance => true)
30
+ CrossStub.clear
31
+ @call["#{@descriptor}.blurb"]
32
+ end
33
+ end
34
+
35
+ should "clear symbol generated stub and return original value" do
36
+ original_value = @instance.say
37
+ @klass.xstub(:say, :instance => true)
38
+ CrossStub.clear
39
+ @call["#{@descriptor}.say"].should.equal original_value
40
+ end
41
+
42
+ should "clear symbol generated stub and raise NoMethodError" do
43
+ should.raise(NoMethodError) do
44
+ @klass.xstub(:blurb, :instance => true)
45
+ CrossStub.clear
46
+ @call["#{@descriptor}.blurb"]
47
+ end
48
+ end
49
+
50
+ should "clear block generated stub and return original value" do
51
+ original_value = @instance.say
52
+ @klass.xstub(:instance => true) do
53
+ def say ; 'HELLO' ; end
54
+ end
55
+ CrossStub.clear
56
+ @call["#{@descriptor}.say"].should.equal original_value
57
+ end
58
+
59
+ should "clear block generated stub and raise NoMethodError" do
60
+ should.raise(NoMethodError) do
61
+ @klass.xstub(:instance => true) do
62
+ def blurb ; 'blah blah' ; end
63
+ end
64
+ CrossStub.clear
65
+ @call["#{@descriptor}.blurb"]
66
+ end
67
+ end
68
+
69
+ should "always clear previously generated stub" do
70
+ original_value = @instance.say
71
+
72
+ # Stub an existing method
73
+ @klass.xstub({:say => 'HELLO'}, :instance => true)
74
+ @call["#{@descriptor}.say"]
75
+
76
+ # Clear stubs without refreshing another process
77
+ CrossStub.clear
78
+ CrossStub.setup(cache_store(@store_type))
79
+
80
+ # Stub a non-existing method
81
+ @klass.xstub({:blurb => 'blah blah'}, :instance => true)
82
+ @call["#{@descriptor}.blurb"]
83
+
84
+ # Make sure existing method returns to original method
85
+ @call["#{@descriptor}.say"].should.equal original_value
86
+ end
87
+
88
+ should "always clear previously generated stub and raise NoMethodError" do
89
+ # Stub a non-existing method
90
+ @klass.xstub({:blurb => 'blah blah'}, :instance => true)
91
+ @call["#{@descriptor}.blurb"]
92
+
93
+ # Clear stubs without refreshing another process
94
+ CrossStub.clear
95
+ CrossStub.setup(cache_store(@store_type))
96
+
97
+ # Stub an existing method
98
+ @klass.xstub({:say => 'HELLO'}, :instance => true)
99
+ @call["#{@descriptor}.say"]
100
+
101
+ # Make sure accessing non-existing method throws error
102
+ should.raise(NoMethodError) { @call["#{@descriptor}.blurb"] }
103
+ end
104
+
105
+ should "clear for method not implemented in ruby and return original value" do
106
+ Time.xstub(:day => 99, :instance => true)
107
+ CrossStub.clear
108
+ value = nil
109
+ should.not.raise(NoMethodError) { value = @call['Time#new.day'] }
110
+ value.should.not.equal 99
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+ end
117
+ end
118
+
119
+ end
@@ -0,0 +1,118 @@
1
+ require File.join(File.dirname(__FILE__), 'shared_spec')
2
+
3
+ describe 'Clearing Stubs' do
4
+
5
+ each_cache_store do |store_type|
6
+ %w{current other}.each do |mode|
7
+ %w{AnyClass AnyClass::Inner AnyModule AnyModule::Inner}.each do |descriptor|
8
+
9
+ describe '>> %s process using :%s store (%s)' % [mode, store_type, descriptor] do
10
+
11
+ before do
12
+ @descriptor = descriptor
13
+ @klass = klassify(descriptor)
14
+ @store_type = store_type
15
+ end
16
+
17
+ behaves_like "has #{mode} process setup"
18
+
19
+ should "clear hash generated stub and return original value" do
20
+ original_value = @klass.say
21
+ @klass.xstub(:say => 'HELLO')
22
+ CrossStub.clear
23
+ @call["#{@descriptor}.say"].should.equal original_value
24
+ end
25
+
26
+ should "clear hash generated stub and raise NoMethodError" do
27
+ should.raise(NoMethodError) do
28
+ @klass.xstub(:bang => 'OOPS')
29
+ CrossStub.clear
30
+ @call["#{@descriptor}.bang"]
31
+ end
32
+ end
33
+
34
+ should "clear symbol generated stub and return original value" do
35
+ original_value = @klass.say
36
+ @klass.xstub(:say)
37
+ CrossStub.clear
38
+ @call["#{@descriptor}.say"].should.equal original_value
39
+ end
40
+
41
+ should "clear symbol generated stub and raise NoMethodError" do
42
+ should.raise(NoMethodError) do
43
+ @klass.xstub(:bang)
44
+ CrossStub.clear
45
+ @call["#{@descriptor}.bang"]
46
+ end
47
+ end
48
+
49
+ should "clear block generated stub and return original value" do
50
+ original_value = @klass.say
51
+ @klass.xstub do
52
+ def say ; 'HELLO' ; end
53
+ end
54
+ CrossStub.clear
55
+ @call["#{@descriptor}.say"].should.equal original_value
56
+ end
57
+
58
+ should "clear block generated stub and raise NoMethodError" do
59
+ should.raise(NoMethodError) do
60
+ @klass.xstub do
61
+ def bang ; 'OOPS' ; end
62
+ end
63
+ CrossStub.clear
64
+ @call["#{@descriptor}.bang"]
65
+ end
66
+ end
67
+
68
+ should "always clear previously generated stub" do
69
+ original_value = @klass.say
70
+
71
+ # Stub an existing method
72
+ @klass.xstub(:say => 'HELLO')
73
+ @call["#{@descriptor}.say"]
74
+
75
+ # Clear stubs without refreshing another process
76
+ CrossStub.clear
77
+ CrossStub.setup(cache_store(@store_type))
78
+
79
+ # Stub a non-existing method
80
+ @klass.xstub(:bang => 'OOPS')
81
+ @call["#{@descriptor}.bang"]
82
+
83
+ # Make sure existing method returns to original method
84
+ @call["#{@descriptor}.say"].should.equal original_value
85
+ end
86
+
87
+ should "always clear previously generated stub and raise NoMethodError" do
88
+ # Stub a non-existing method
89
+ @klass.xstub(:bang => 'OOPS')
90
+ @call["#{@descriptor}.bang"]
91
+
92
+ # Clear stubs without refreshing another process
93
+ CrossStub.clear
94
+ CrossStub.setup(cache_store(@store_type))
95
+
96
+ # Stub an existing method
97
+ @klass.xstub(:say => 'HELLO')
98
+ @call["#{@descriptor}.say"]
99
+
100
+ # Make sure accessing non-existing method throws error
101
+ should.raise(NoMethodError) { @call["#{@descriptor}.bang"] }
102
+ end
103
+
104
+ should "clear for method not implemented in ruby and return original value" do
105
+ Time.xstub(:now => 'abc')
106
+ CrossStub.clear
107
+ value = nil
108
+ should.not.raise(NoMethodError) { value = @call['Time.now'] }
109
+ value.should.not.equal 'abc'
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+
118
+ end
@@ -0,0 +1,91 @@
1
+ require File.join(File.dirname(__FILE__), 'shared_spec')
2
+
3
+ describe 'Creating Instance Stubs' do
4
+
5
+ each_cache_store do |store_type|
6
+ %w{current other}.each do |mode|
7
+ %w{AnyInstance#new AnyInstance::Inner#new}.each do |descriptor|
8
+
9
+ describe '>> %s process using :%s store (%s)' % [mode, store_type, descriptor] do
10
+
11
+ before do
12
+ @descriptor = descriptor
13
+ @klass = klassify(descriptor)
14
+ @store_type = store_type
15
+ end
16
+
17
+ behaves_like "has #{mode} process setup"
18
+
19
+ should "create with hash argument(s)" do
20
+ @klass.xstub({:bang => 'OOPS', :say => 'HELLO'}, :instance => true)
21
+ @call["#{@descriptor}.bang"].should.equal 'OOPS'
22
+ @call["#{@descriptor}.say"].should.equal 'HELLO'
23
+ end
24
+
25
+ should "create with symbol argument(s)" do
26
+ @klass.xstub(:bang, :instance => true)
27
+ @call["#{@descriptor}.bang"].should.equal nil
28
+ end
29
+
30
+ should "create with block with no argument" do
31
+ @klass.xstub(:instance => true) do
32
+ def bang ; 'OOPS' ; end
33
+ end
34
+ @call["#{@descriptor}.bang"].should.equal 'OOPS'
35
+ end
36
+
37
+ should "create with symbol & block with no argument" do
38
+ @klass.xstub(:bang, :instance => true) do
39
+ def say ; 'HELLO' ; end
40
+ end
41
+ @call["#{@descriptor}.bang"].should.equal nil
42
+ @call["#{@descriptor}.say"].should.equal 'HELLO'
43
+ end
44
+
45
+ should "create with hash & block with no argument" do
46
+ @klass.xstub({:bang => 'OOPS'}, :instance => true) do
47
+ def say ; 'HELLO' ; end
48
+ end
49
+ @call["#{@descriptor}.bang"].should.equal 'OOPS'
50
+ @call["#{@descriptor}.say"].should.equal 'HELLO'
51
+ end
52
+
53
+ should "always create the most recent" do
54
+ found, expected = [], ['OOPS', 'OOOPS', 'OOOOPS']
55
+ stub_and_get_value = lambda do |value|
56
+ @klass.xstub({:bang => value}, :instance => true)
57
+ @call["#{@descriptor}.bang"]
58
+ end
59
+
60
+ found << stub_and_get_value[expected[0]]
61
+ found << stub_and_get_value[expected[1]]
62
+
63
+ CrossStub.clear
64
+ CrossStub.setup(cache_store(@store_type))
65
+
66
+ found << stub_and_get_value[expected[2]]
67
+ found.should.equal expected
68
+ end
69
+
70
+ should "create stub with dependency on other stub" do
71
+ @klass.xstub({:something => 'HELLO'}, :instance => true) do
72
+ def do_action(who, action)
73
+ %\#{who} #{action} #{something}\
74
+ end
75
+ end
76
+ @call["#{@descriptor}.do_action.i.say"].should.equal 'i say HELLO'
77
+ end
78
+
79
+ should "create for method not implemented in ruby" do
80
+ day = 99
81
+ Time.xstub({:day => day}, :instance => true)
82
+ @call['Time#new.day'].should.equal day
83
+ end
84
+
85
+ end
86
+
87
+ end
88
+ end
89
+ end
90
+
91
+ end
@@ -0,0 +1,95 @@
1
+ require File.join(File.dirname(__FILE__), 'shared_spec')
2
+
3
+ describe 'Creating Stubs' do
4
+
5
+ each_cache_store do |store_type|
6
+ %w{current other}.each do |mode|
7
+ %w{AnyClass AnyModule AnyClass::Inner AnyModule::Inner}.each do |descriptor|
8
+
9
+ describe '>> %s process using :%s store (%s)' % [mode, store_type, descriptor] do
10
+
11
+ before do
12
+ @descriptor = descriptor
13
+ @klass = klassify(descriptor)
14
+ @store_type = store_type
15
+ end
16
+
17
+ behaves_like "has #{mode} process setup"
18
+
19
+ should "create with hash argument(s)" do
20
+ @klass.xstub(:bang => 'OOPS', :say => 'HELLO')
21
+ @call["#{@descriptor}.bang"].should.equal 'OOPS'
22
+ @call["#{@descriptor}.say"].should.equal 'HELLO'
23
+ end
24
+
25
+ should "create with symbol argument(s)" do
26
+ @klass.xstub(:bang)
27
+ @call["#{@descriptor}.bang"].should.equal nil
28
+ end
29
+
30
+ should "create with block with no argument" do
31
+ @klass.xstub do
32
+ def bang ; 'OOPS' ; end
33
+ end
34
+ @call["#{@descriptor}.bang"].should.equal 'OOPS'
35
+ end
36
+
37
+ should "create with symbol & block with no argument" do
38
+ @klass.xstub(:bang) do
39
+ def say
40
+ 'HELLO'
41
+ end
42
+ end
43
+ @call["#{@descriptor}.bang"].should.equal nil
44
+ @call["#{@descriptor}.say"].should.equal 'HELLO'
45
+ end
46
+
47
+ should "create with hash & block with no argument" do
48
+ @klass.xstub(:bang => 'OOPS') do
49
+ def say
50
+ 'HELLO'
51
+ end
52
+ end
53
+ @call["#{@descriptor}.bang"].should.equal 'OOPS'
54
+ @call["#{@descriptor}.say"].should.equal 'HELLO'
55
+ end
56
+
57
+ should "always create the most recent" do
58
+ found, expected = [], ['OOPS', 'OOOPS', 'OOOOPS']
59
+ stub_and_get_value = lambda do |value|
60
+ @klass.xstub(:bang => value)
61
+ @call["#{@descriptor}.bang"]
62
+ end
63
+
64
+ found << stub_and_get_value[expected[0]]
65
+ found << stub_and_get_value[expected[1]]
66
+
67
+ CrossStub.clear
68
+ CrossStub.setup(cache_store(@store_type))
69
+
70
+ found << stub_and_get_value[expected[2]]
71
+ found.should.equal expected
72
+ end
73
+
74
+ should "create stub with dependency on other stub" do
75
+ @klass.xstub(:something => 'HELLO') do
76
+ def do_action(who, action)
77
+ %\#{who} #{action} #{something}\
78
+ end
79
+ end
80
+ @call["#{@descriptor}.do_action.i.say"].should.equal 'i say HELLO'
81
+ end
82
+
83
+ should "create for method not implemented in ruby" do
84
+ now = Time.now - 365*60*60*24
85
+ Time.xstub(:now => now)
86
+ @call['Time.now'].should.equal now
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+ end
93
+ end
94
+
95
+ end