README.rdoc in cross-stub-0.1.4 vs README.rdoc in cross-stub-0.2.0

- old
+ new

@@ -1,10 +1,10 @@ = CROSS-STUB makes cross process stubbing possible !! == Introduction -Existing mocking/stubbing frameworks support only stubbing in the current process. This is OK most of the time. However, when running cucumber integration test suite in non-webrat mode, these in-process stubbing frameworks simply doesn't help. Eg. I want Time.now to always return a timing that should be a Sunday, how do I do that when running cucumber in selenium, culerity, steam, blah, blah mode? It doesn't seem straight-forward me. +Existing mocking/stubbing frameworks support only stubbing in the current process. This is OK most of the time. However, when running cucumber integration test suite in another process, these in-process stubbing frameworks simply doesn't help. Eg. I want Time.now to always return a timing that should be a Sunday, how do I do that when running cucumber using selenium, culerity, steam, blah, blah driver? It doesn't seem straight-forward me. (Let's not argue whether stubbing should be encouraged. It is an itch, the poor itch needs to be scratched.) == Getting Started @@ -12,90 +12,146 @@ $ sudo gem install cross-stub == Setting Up -#1. If you are using rails, you are in luck: +=== #1. Rails: $ ./script/generate cucumber $ ./script/generate cross_stub -#2. Even if you are using something else, no worries: +=== #2. Others (back to basics): # In the test setup method: - CrossStub.setup :file => <CACHE_FILE_PATH> + CrossStub.setup :file => <CACHE_FILE> # In the test teardown method: CrossStub.clear # Find an entry point in your target application, eg. in a server, the # point where all request handling starts: - CrossStub.refresh :file => <CACHE_FILE_PATH> + CrossStub.refresh :file => <CACHE_FILE> +For a full list of available cache stores, scroll down to take a look at the 'Cache Stores' section. + == Using It -Using cross-stub is simple: +Cross-stubbing is simple: -#1. Simple returning of nil or non-nil value: +=== #1. Simple returning of nil or non-nil value: +==== #1.1. Class method: + class Someone - def self.laugh - 'HaHa' + def self.say + 'hello' end end - Someone.xstub(:laugh) - Someone.laugh # yields: nil + Someone.xstub(:say) + Someone.say # yields: nil - Someone.xstub(:laugh, 'HoHo') - Someone.laugh # yields: 'HoHo' + Someone.xstub(:say => 'HELLO') + Someone.say # yields: 'HELLO' -#2. If a stubbed method requires argument, pass :xstub a proc: +==== #1.2. Instance method: + Someone.xstub(:say, :instance => true) + Someone.new.say # yields: nil + + Someone.xstub({:say => 'HELLO'}, :instance => true) + Someone.new.say # yields: 'HELLO' + +=== #2. If a stubbed method requires argument, pass xstub a proc: + +==== #2.1. Class method: + Someone.xstub do - def loves(other) - "I love #{other}" + def say(something) + 'saying "%s"' % something end end - Someone.loves('you') # yields: 'I love you' + Someone.say('HELLO') # yields: 'saying "HELLO"' -#3. Something more complicated: +==== #2.2. Instance method: + Someone.xstub(:instance => true) do + def say(something) + 'saying "%s"' % something + end + end + + Someone.new.say('HELLO') # yields: 'saying "HELLO"' + +IMPORTANT: Since switching from ParseTree to RubyParser, dynamic code analysis is no longer possible. When defining methods in the proc, fanciful coding is strongly discouraged, pls follow the conservative style suggested above. + +=== #3. Something more complicated: + something = 'hello' Someone.xstub do - def do_action(who, action) - %\#{who} #{action} #{something}\ + def say + 'saying "%s"' % something end end - Someone.do_action('i', 'say') # failure !! + Someone.say # failure !! The above fails as a result of undefined variable/method 'something', to workaround we can have: - Someone.xstub(:something => 'hello') do - def do_action(who, action) - %\#{who} #{action} #{something}\ + Someone.xstub(:something => 'HELLO') do + def say + 'saying "%s"' % something end end - Someone.do_action('i', 'say') # yields: 'i say hello' + Someone.say # yields: 'saying "HELLO"' +== Cache Stores + +Cache stores are needed to allow stubs to be made available for different processes. The following describes all cache stores available: + +=== #1. File + + # Setting up (current process) + CrossStub.setup :file => '<CACHE_FILE>' + + # Refreshing (other process) + CrossStub.refresh :file => '<CACHE_FILE>' + +=== #2. Memcache (requires memcache-client gem) + + # Setting up (current process) + CrossStub.setup :memcache => 'localhost:11211/<CACHE_ID>' + + # Refreshing (other process) + CrossStub.refresh :memcache => 'localhost:11211/<CACHE_ID>' + +=== #3. Redis (requires redis gem) + + # Setting up (current process) + CrossStub.setup :redis => 'localhost:6379/<CACHE_ID>' + + # Refreshing (other process) + CrossStub.refresh :redis => 'localhost:6379/<CACHE_ID>' + +Adding new store is super easy (w.r.t testing & actual implementation), let me know if u need more :] + == Caveats #1. Cross-stub uses ruby's Marshal class to dump & load the stubs, thus it has the same limitations as Marshal -#2. Cross-stub only supports stubbing of class methods, since it makes no sense to do cross process stubbing of instances +#2. Having switched from ParseTree to RubyParser, dynamic code analysis is no longer available, the proc taken by xstub no longer supports fanciful coding, pls follow the conservative style suggested in this README. == TODO(s) #1. We can possibly use Hijack (http://github.com/ileitch/hijack) within the current test process to trigger refreshing of stubs in the other process, thus using cross-stub no longer requires changing of any existing application code to insert CrossStub.refresh(...). This has in fact been our original intended strategy, however, we have not been able to do so as Hijack currently hangs on starting up on our development machines. == Contacts -Written 2009 by: +Written since 2009 by: -#1. NgTzeYang, contact ngty77[at]gmail.com or http://github.com/ngty +#1. NgTzeYang, contact ngty77[at]gmail[dot]com or http://github.com/ngty -#2. WongLiangZan, contact liangzan[at]gmail.com or http://github.com/liangzan +#2. WongLiangZan, contact liangzan[at]gmail[dot]com or http://github.com/liangzan Released under the MIT license