cross-stub 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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