From fred at 82ask.com Sat Mar 3 11:06:58 2007 From: fred at 82ask.com (Frederick Cheung) Date: Sat, 3 Mar 2007 16:06:58 +0000 Subject: [mocha-developer] Beyond multiple return values Message-ID: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> First off, I love mocha and have been using it all over the place ever since I found it a few months ago. So I noticed the other day rather belatedly that mocha-0.4.0 had been released and that we can now do the object.stubs(:method).returns (:first_value, :second_value). Much neater than fiddling with lambdas everytime this sort of behaviour is needed But can we go further ? I was writing a test today and I wanted the first call to a method to raise an exception and the next call to return a value (I was testing a method on an ActiveRecord object where if save! raises StaleObjectError then we reload and retry in the proper way, the second call to save! shouldn't raise.). The same basic method works: lambdas = [lambda {raise 'Foo'}, lambda {return true}] object.stubs(:save!).returns {lambda {lambdas.shift.call}} It's a lot less readable than your average lovely bit of mocha based test. Can anyone think of a way of doing this that looks nice? Fred From fred at 82ask.com Sun Mar 4 06:41:35 2007 From: fred at 82ask.com (Frederick Cheung) Date: Sun, 4 Mar 2007 11:41:35 +0000 Subject: [mocha-developer] Rails functional testing and Mocha Message-ID: <599A8614-0A22-4869-B4BB-7CAA35A81DDF@82ask.com> I've always wanted to be able to do stuff like this in my functional tests c = customers(:customer_1) c.expects(:great_customer_service) post :service_customer, :id => c.id This of course fails because inside the rails action a different instance of customer is used. Some of the time setting your expectation/stubbing on Customer.any_instance works, but it's not beautiful and of course breaks down if there are multiple customers installed. So I mucked around a bit and came of with the following. It's very ActiveRecord specific, but that's what I've been dealing with.. I have a model Customer, and concrete subclass EightTwoAskCustomer class EightTwoAskCustomer < Customer def bill #take all their money end end A controller: class TestController < ApplicationController def bill c = Customer.find params[:id] c.bill render :nothing => true end end A functional test: [snip boiler plate] require 'eight_two_ask_customer' class EightTwoAskCustomer include Mocha::ExpectationLoader end [snip more boiler plate] # Replace this with your real tests. def test_bill_customer c = customers(:customer1) EightTwoAskCustomer.any_instance_with_id( c.id).expects(:bill) post :bill, :id => c.id end This does mostly what I want, the test passes. If I comment out c.bill I get a failure: #.bill - expected calls: 1, actual calls: 0 If I try to increase gross margins by billing the customer twice I get a failure too: #.bill - expected calls: 1, actual calls: 2 Implementation wise it looks like this: A intermediary class AnyInstanceWithID. A method is defined on Class that provides you with instances of it def any_instance_with_id the_id @AnyInstances = {} unless defined? @AnyInstances @AnyInstances[the_id] ||= AnyInstanceWithID.new() end AnyInstanceWithID is a sort of proxy thing on which you set expectations. It keeps them until it can set them on the real things. It looks very much like Mocha::Mock (there's some refactoring to be done here), except that it doesn't actually do anything in terms of undefining methods. class AnyInstanceWithID attr_reader :expectations def initialize @expectations = [] end def stubs(method_names) method_names = method_names.is_a?(Hash) ? method_names : { method_names => nil } method_names.each do |method_name, return_value| expectations << Mocha::Stub.new(nil, method_name, caller).returns(return_value) end expectations.last end def expects(method_names) method_names = method_names.is_a?(Hash) ? method_names : { method_names => nil } method_names.each do |method_name, return_value| expectations << Mocha::Expectation.new(nil, method_name, caller).returns(return_value) end expectations.last end end Lastly we define an implementation of after_find that causes the expectations to be set as the objects are loaded module Mocha module ExpectationLoader def after_find if any = self.class.any_instance_with_id( self.id) any.expectations.each do |e| method = stubba_method.new(stubba_object, e.method_name) $stubba.stub(method) e.mock = self.mocha self.mocha.expectations << e self.mocha.__metaclass__.send(:undef_method, e.method_name) if self.mocha.__metaclass__.method_defined? (e.method_name) end end end end end There's also one or 2 places where i've made attributes writable/ readable on Expectation etc... to get this all to hold together. Limitations: - The big limitation is that if an instance with the required id is not loaded than the test does not fail (since the expectation was never set). There may well be other nasties lurking. - The syntax is also rather awkward, but I'm sure someone will think of a nice way to name it all - I should be a good boy and check if after_find is already defined and call back into the existing one - Behaviour w.r.t subclasses is a little confusing: I have to set my expectation on EightTwoAskCustomer.any_instance_with_id, setting it on the parent class Customer doesn't work - would be nice if using any_instance_with_id caused it to include ExpectationLoader for you. So, first of all: am I completely off my rocker ? Please tell me if I'm wasting my time/ missing the point/ missing a more obvious way of accomplishing this - Any ideas for tidying up some of the limitations ? Anyway, sorry for the long email, I look forward to hearing any comments. Fred From dchelimsky at gmail.com Sun Mar 4 07:47:35 2007 From: dchelimsky at gmail.com (David Chelimsky) Date: Sun, 4 Mar 2007 06:47:35 -0600 Subject: [mocha-developer] Rails functional testing and Mocha In-Reply-To: <599A8614-0A22-4869-B4BB-7CAA35A81DDF@82ask.com> References: <599A8614-0A22-4869-B4BB-7CAA35A81DDF@82ask.com> Message-ID: <57c63afe0703040447t681e82e9m68236f6484df9c7f@mail.gmail.com> On 3/4/07, Frederick Cheung wrote: > I've always wanted to be able to do stuff like this in my functional > tests > > c = customers(:customer_1) > c.expects(:great_customer_service) > post :service_customer, :id => c.id FWIW, I like to handle this sort of thing like this: mock_customer = mock mock_customer.expects(:great_customer_service) Customer.expects(:find).with("37").returns(mock_customer) post :service_customer, :id => "37" This works if your viewpoint is that rails functionals are really "controller and view" tests and that they shouldn't depend on real models (which is my viewpoint). Even if you think real models should be in your functionals, you can do it this way: customer = customers(:customer_1) customer.expects(:great_customer_service) Customer.expects(:find).with(customer.id.to_s).returns(c) post :service_customer, :id => customer.id This is not to suggest that you're approach is wrong or that the one I propose is "right". It just aligns better w/ my personal views. And it allows you to do what you want without monkey patching mocha. Cheers, David > > This of course fails because inside the rails action a different > instance of customer is used. Some of the time setting your > expectation/stubbing on Customer.any_instance works, but it's not > beautiful and of course breaks down if there are multiple customers > installed. > > So I mucked around a bit and came of with the following. It's very > ActiveRecord specific, but that's what I've been dealing with.. > I have a model Customer, and concrete subclass EightTwoAskCustomer > > class EightTwoAskCustomer < Customer > def bill > #take all their money > end > end > > A controller: > > class TestController < ApplicationController > def bill > c = Customer.find params[:id] > c.bill > render :nothing => true > end > end > > A functional test: > [snip boiler plate] > > require 'eight_two_ask_customer' > class EightTwoAskCustomer > include Mocha::ExpectationLoader > end > > [snip more boiler plate] > # Replace this with your real tests. > def test_bill_customer > c = customers(:customer1) > EightTwoAskCustomer.any_instance_with_id( c.id).expects(:bill) > post :bill, :id => c.id > end > > This does mostly what I want, the test passes. If I comment out > c.bill I get a failure: > #.bill - expected calls: 1, actual calls: 0 > If I try to increase gross margins by billing the customer twice I > get a failure too: > #.bill - expected calls: 1, actual calls: 2 > > Implementation wise it looks like this: > > A intermediary class AnyInstanceWithID. A method is defined on Class > that provides you with instances of it > > def any_instance_with_id the_id > @AnyInstances = {} unless defined? @AnyInstances > @AnyInstances[the_id] ||= AnyInstanceWithID.new() > end > > AnyInstanceWithID is a sort of proxy thing on which you set > expectations. It keeps them until it can set them on the real > things. It looks very much like Mocha::Mock (there's some > refactoring to be done here), except that it doesn't actually do > anything in terms of undefining methods. > > class AnyInstanceWithID > attr_reader :expectations > def initialize > @expectations = [] > end > > def stubs(method_names) > method_names = method_names.is_a?(Hash) ? method_names : > { method_names => nil } > method_names.each do |method_name, return_value| > expectations << Mocha::Stub.new(nil, method_name, > caller).returns(return_value) > end > expectations.last > end > > def expects(method_names) > method_names = method_names.is_a?(Hash) ? method_names : > { method_names => nil } > method_names.each do |method_name, return_value| > expectations << Mocha::Expectation.new(nil, method_name, > caller).returns(return_value) > end > expectations.last > end > end > > Lastly we define an implementation of after_find that causes the > expectations to be set as the objects are loaded > > module Mocha > module ExpectationLoader > def after_find > if any = self.class.any_instance_with_id( self.id) > any.expectations.each do |e| > method = stubba_method.new(stubba_object, e.method_name) > $stubba.stub(method) > e.mock = self.mocha > self.mocha.expectations << e > self.mocha.__metaclass__.send(:undef_method, > e.method_name) if self.mocha.__metaclass__.method_defined? > (e.method_name) > end > end > end > end > end > > There's also one or 2 places where i've made attributes writable/ > readable on Expectation etc... to get this all to hold together. > > Limitations: > - The big limitation is that if an instance with the required id is > not loaded than the test does not fail (since the expectation was > never set). There may well be other nasties lurking. > - The syntax is also rather awkward, but I'm sure someone will think > of a nice way to name it all > - I should be a good boy and check if after_find is already defined > and call back into the existing one > - Behaviour w.r.t subclasses is a little confusing: I have to set my > expectation on EightTwoAskCustomer.any_instance_with_id, setting it > on the parent class Customer doesn't work > - would be nice if using any_instance_with_id caused it to include > ExpectationLoader for you. > > > So, first of all: am I completely off my rocker ? Please tell me if > I'm wasting my time/ missing the point/ missing a more obvious way of > accomplishing this > - Any ideas for tidying up some of the limitations ? > > Anyway, sorry for the long email, I look forward to hearing any > comments. > > Fred > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer > From fred at 82ask.com Sun Mar 4 08:16:11 2007 From: fred at 82ask.com (Frederick Cheung) Date: Sun, 4 Mar 2007 13:16:11 +0000 Subject: [mocha-developer] Rails functional testing and Mocha In-Reply-To: <57c63afe0703040447t681e82e9m68236f6484df9c7f@mail.gmail.com> References: <599A8614-0A22-4869-B4BB-7CAA35A81DDF@82ask.com> <57c63afe0703040447t681e82e9m68236f6484df9c7f@mail.gmail.com> Message-ID: On 4 Mar 2007, at 12:47, David Chelimsky wrote: > On 3/4/07, Frederick Cheung wrote: >> I've always wanted to be able to do stuff like this in my functional >> tests >> >> c = customers(:customer_1) >> c.expects(:great_customer_service) >> post :service_customer, :id => c.id > > FWIW, I like to handle this sort of thing like this: > > mock_customer = mock > mock_customer.expects(:great_customer_service) > Customer.expects(:find).with("37").returns(mock_customer) > post :service_customer, :id => "37" > > This works if your viewpoint is that rails functionals are really > "controller and view" tests and that they shouldn't depend on real > models (which is my viewpoint). Even if you think real models should > be in your functionals, you can do it this way: > > customer = customers(:customer_1) > customer.expects(:great_customer_service) > Customer.expects(:find).with(customer.id.to_s).returns(c) > post :service_customer, :id => customer.id That's neat, definitely a nice approach when it fits in (and I can certainly see why you would have the viewpoint you do). Thanks, Fred From jamesmead44 at gmail.com Mon Mar 5 06:06:45 2007 From: jamesmead44 at gmail.com (James Mead) Date: Mon, 5 Mar 2007 11:06:45 +0000 Subject: [mocha-developer] Beyond multiple return values In-Reply-To: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> References: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> Message-ID: <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> On 03/03/07, Frederick Cheung wrote: > > First off, I love mocha and have been using it all over the place > ever since I found it a few months ago. Good stuff. So I noticed the other day rather belatedly that mocha-0.4.0 had been > released and that we can now do the object.stubs(:method).returns > (:first_value, :second_value). Much neater than fiddling with lambdas > everytime this sort of behaviour is needed > > But can we go further ? I was writing a test today and I wanted the > first call to a method to raise an exception and the next call to > return a value (I was testing a method on an ActiveRecord object > where if save! raises StaleObjectError then we reload and retry in > the proper way, the second call to save! shouldn't raise.). > > The same basic method works: > > lambdas = [lambda {raise 'Foo'}, lambda {return true}] > object.stubs(:save!).returns {lambda {lambdas.shift.call}} > > It's a lot less readable than your average lovely bit of mocha based > test. > > Can anyone think of a way of doing this that looks nice? > Interesting. Perhaps we can at least do this... object.stubs(:save!).returns(lambda { raise 'Foo' }, true ) Or (if we add a 'raises' method to TestCase)... object.stubs(:save!).returns(raises(), true) Thoughts anyone? -- James. http://blog.floehopper.org From jamesmead44 at gmail.com Mon Mar 5 06:15:19 2007 From: jamesmead44 at gmail.com (James Mead) Date: Mon, 5 Mar 2007 11:15:19 +0000 Subject: [mocha-developer] Rails functional testing and Mocha In-Reply-To: References: <599A8614-0A22-4869-B4BB-7CAA35A81DDF@82ask.com> <57c63afe0703040447t681e82e9m68236f6484df9c7f@mail.gmail.com> Message-ID: <1db558f00703050315r3fb13ff3x6d6fb9743a640f27@mail.gmail.com> Hi Fred, It's great to see you playing around with the internals of Mocha. I hope you've been having fun! I use the same technique that David describes (although I'd probably stub the call to find not expect it, because its a query not a command). In fact I rarely use the any_instance form of stubbing. It feels a bit dirty! I don't think we'd want to incorporate the change you're suggesting, because (as you say) its a bit ActiveRecord-specific and there is an alternative. Is there anything you don't like about the technique David suggests (i.e. stubbing the find method)? -- James. http://blog.floehopper.org From fred at 82ask.com Mon Mar 5 06:37:06 2007 From: fred at 82ask.com (Frederick Cheung) Date: Mon, 5 Mar 2007 11:37:06 +0000 Subject: [mocha-developer] Beyond multiple return values In-Reply-To: <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> References: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> Message-ID: On 5 Mar 2007, at 11:06, James Mead wrote: >> The same basic method works: >> >> lambdas = [lambda {raise 'Foo'}, lambda {return true}] >> object.stubs(:save!).returns {lambda {lambdas.shift.call}} >> >> It's a lot less readable than your average lovely bit of mocha based >> test. >> >> Can anyone think of a way of doing this that looks nice? >> > > Interesting. Perhaps we can at least do this... > > object.stubs(:save!).returns(lambda { raise 'Foo' }, true ) > > Or (if we add a 'raises' method to TestCase)... > > object.stubs(:save!).returns(raises(), true) Someone a little while back proposed added a then() method. It was a little more cumbersome than returns(:foo_1, :foo_2) but object.stubs(:save!).raises('Foo').then.returns(true) isn't too bad. Fred From fred at 82ask.com Mon Mar 5 06:48:48 2007 From: fred at 82ask.com (Frederick Cheung) Date: Mon, 5 Mar 2007 11:48:48 +0000 Subject: [mocha-developer] Rails functional testing and Mocha In-Reply-To: <1db558f00703050315r3fb13ff3x6d6fb9743a640f27@mail.gmail.com> References: <599A8614-0A22-4869-B4BB-7CAA35A81DDF@82ask.com> <57c63afe0703040447t681e82e9m68236f6484df9c7f@mail.gmail.com> <1db558f00703050315r3fb13ff3x6d6fb9743a640f27@mail.gmail.com> Message-ID: On 5 Mar 2007, at 11:15, James Mead wrote: > Hi Fred, > > It's great to see you playing around with the internals of Mocha. I > hope > you've been having fun! > > I use the same technique that David describes (although I'd > probably stub > the call to find not expect it, because its a query not a command). > In fact I rarely use the any_instance form of stubbing. It feels a bit > dirty! > > I don't think we'd want to incorporate the change you're > suggesting, because > (as you say) its a bit ActiveRecord-specific and there is an > alternative. > > Is there anything you don't like about the technique David suggests > (i.e. > stubbing the find method)? > I like it very much actually. In general though I am a little nervous about testing everything in isolation i.e. models in their unit tests controllers & views in their functional etc... I like knowing that I've taken a complete path through the system. Perhaps some of my functional tests should actually be integration tests but that doesn't really make a distance here. I can also see it getting a little hairy if you're faking up the finding of multiple objects, with associations etc... Fred From dchelimsky at gmail.com Mon Mar 5 08:04:53 2007 From: dchelimsky at gmail.com (David Chelimsky) Date: Mon, 5 Mar 2007 07:04:53 -0600 Subject: [mocha-developer] When to stub/when to mock (was Rails functional testing and Mocha) Message-ID: <57c63afe0703050504n37e5c243m9ca66dee27dbc518@mail.gmail.com> Hi James, > From: James Mead > Date: Mar 5, 2007 5:15 AM > Subject: Re: [mocha-developer] Rails functional testing and Mocha > To: mocha-developer at rubyforge.org > "I'd probably stub the call to find not expect it, because its a query not a command" I have a different perspective on when to use stubs and when to use mocks and I'm curious as to your thoughts about it. I like to put stubs in setup, regardless of whether they are commands or queries, and mock expectations in test methods (specify blocks in rspec). This keeps the noise of what is necessary but uninteresting (from the perspective of the tests) out of the way of the test methods, which can focus entirely on the interesting bits. The motivation is the same as Nat Pryce's - expecting only the interesting bits will make tests less brittle - but the resulting principle revolves on a different axis. Thoughts on this? Cheers, David From jamesmead44 at gmail.com Mon Mar 5 10:52:44 2007 From: jamesmead44 at gmail.com (James Mead) Date: Mon, 5 Mar 2007 15:52:44 +0000 Subject: [mocha-developer] When to stub/when to mock (was Rails functional testing and Mocha) In-Reply-To: <57c63afe0703050504n37e5c243m9ca66dee27dbc518@mail.gmail.com> References: <57c63afe0703050504n37e5c243m9ca66dee27dbc518@mail.gmail.com> Message-ID: <1db558f00703050752s5ab99dfq2512a419577fd591@mail.gmail.com> On 05/03/07, David Chelimsky wrote: > > I have a different perspective on when to use stubs and when to use > mocks and I'm curious as to your thoughts about it. > > I like to put stubs in setup, regardless of whether they are commands > or queries, and mock expectations in test methods (specify blocks in > rspec). This keeps the noise of what is necessary but uninteresting > (from the perspective of the tests) out of the way of the test > methods, which can focus entirely on the interesting bits. > > The motivation is the same as Nat Pryce's - expecting only the > interesting bits will make tests less brittle - but the resulting > principle revolves on a different axis. > > Thoughts on this? > Hi David, I don't think we are thinking very differently. I think of the stubs as providing an environment for the object under test. Whether and how the object under test chooses to query this environment is an implementation detail. So I stub rather than expect. Does that make sense? I'm not sure what you mean by "revolving on a different axis". -- James. http://blog.floehopper.org From dchelimsky at gmail.com Mon Mar 5 11:21:43 2007 From: dchelimsky at gmail.com (David Chelimsky) Date: Mon, 5 Mar 2007 10:21:43 -0600 Subject: [mocha-developer] When to stub/when to mock (was Rails functional testing and Mocha) In-Reply-To: <1db558f00703050752s5ab99dfq2512a419577fd591@mail.gmail.com> References: <57c63afe0703050504n37e5c243m9ca66dee27dbc518@mail.gmail.com> <1db558f00703050752s5ab99dfq2512a419577fd591@mail.gmail.com> Message-ID: <57c63afe0703050821webad23bpa6052e08e72a4236@mail.gmail.com> On 3/5/07, James Mead wrote: > On 05/03/07, David Chelimsky wrote: > > > > I have a different perspective on when to use stubs and when to use > > mocks and I'm curious as to your thoughts about it. > > > > I like to put stubs in setup, regardless of whether they are commands > > or queries, and mock expectations in test methods (specify blocks in > > rspec). This keeps the noise of what is necessary but uninteresting > > (from the perspective of the tests) out of the way of the test > > methods, which can focus entirely on the interesting bits. > > > > The motivation is the same as Nat Pryce's - expecting only the > > interesting bits will make tests less brittle - but the resulting > > principle revolves on a different axis. > > > > Thoughts on this? > > > > Hi David, > > I don't think we are thinking very differently. I'm quite confident that our goals are the same - readable and robust (non-brittle) tests. > I think of the stubs as providing an environment for the object under test. > Whether and how the object under test chooses to query this environment is > an implementation detail. So I stub rather than expect. Does that make > sense? > > I'm not sure what you mean by "revolving on a different axis". Sorry - that was a vague metaphor. What I mean is that you're using the "what" (query vs command) to decide whether to stub or expect, whereas I use the "where" (setup vs test method). I approach it this way for readability. I want to be able to look at a test method and not have to look anywhere else to understand what it is specifying. One weird side-effect is that I often end up stubbing things in setup that I then "expect" explicitly in the test method. That way the other tests using the same setup are guaranteed (well, more likely ;) ) to run, but the tests that are actually interested in a particular detail express that expectation explicitly. Make sense? > > -- > James. > http://blog.floehopper.org > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer > From jamesmead44 at gmail.com Mon Mar 5 11:51:22 2007 From: jamesmead44 at gmail.com (James Mead) Date: Mon, 5 Mar 2007 16:51:22 +0000 Subject: [mocha-developer] When to stub/when to mock (was Rails functional testing and Mocha) In-Reply-To: <57c63afe0703050821webad23bpa6052e08e72a4236@mail.gmail.com> References: <57c63afe0703050504n37e5c243m9ca66dee27dbc518@mail.gmail.com> <1db558f00703050752s5ab99dfq2512a419577fd591@mail.gmail.com> <57c63afe0703050821webad23bpa6052e08e72a4236@mail.gmail.com> Message-ID: <1db558f00703050851n217283f0q1f992ad873e83180@mail.gmail.com> Yes - I understand what you are doing and agree that our goals are the same. Query vs Command isn't the only way I decide whether to stub or expect. I (nearly) always stub Queries, but I don't always expect Commands. Using the "one assertion per test" idea I select at most one Command to expect per test. Also I don't tend to use a setup method. -- James. http://blog.floehopper.org From dchelimsky at gmail.com Mon Mar 5 12:03:13 2007 From: dchelimsky at gmail.com (David Chelimsky) Date: Mon, 5 Mar 2007 11:03:13 -0600 Subject: [mocha-developer] When to stub/when to mock (was Rails functional testing and Mocha) In-Reply-To: <1db558f00703050851n217283f0q1f992ad873e83180@mail.gmail.com> References: <57c63afe0703050504n37e5c243m9ca66dee27dbc518@mail.gmail.com> <1db558f00703050752s5ab99dfq2512a419577fd591@mail.gmail.com> <57c63afe0703050821webad23bpa6052e08e72a4236@mail.gmail.com> <1db558f00703050851n217283f0q1f992ad873e83180@mail.gmail.com> Message-ID: <57c63afe0703050903q13150607y4ac124a890b0e1f0@mail.gmail.com> On 3/5/07, James Mead wrote: > Yes - I understand what you are doing and agree that our goals are the same. > > Query vs Command isn't the only way I decide whether to stub or expect. I > (nearly) always stub Queries, but I don't always expect Commands. Using the > "one assertion per test" idea I select at most one Command to expect per > test. Also I don't tend to use a setup method. Cool. Thanks for the feedback. Cheers, David > > -- > James. > http://blog.floehopper.org > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer > From jamesthepiper at gmail.com Mon Mar 5 16:27:22 2007 From: jamesthepiper at gmail.com (James Moore) Date: Mon, 5 Mar 2007 13:27:22 -0800 Subject: [mocha-developer] Beyond multiple return values In-Reply-To: <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> References: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> Message-ID: <7cfed280703051327v7f7b6405rbc42d423ff447c7c@mail.gmail.com> How about saying that if what is passed to returns() is an exception, or a class that's a descendant of Exception, it's raised instead of returned? You could still return an exception by returning it from a lambda. obj.stubs(:save!).returns(RuntimeError.new("testing..."), true) # Raises, true obj.stubs(:save!).returns(RuntimeError, true) # Raises, true obj.stubs(:save!).returns(lambda {RuntimeError}, true) # returns the exception object, true Adding raises() to TestObject seems a bit intrusive. - James Moore From dan at tastapod.com Tue Mar 6 05:42:57 2007 From: dan at tastapod.com (Dan North) Date: Tue, 06 Mar 2007 10:42:57 +0000 Subject: [mocha-developer] mocking missing methods Message-ID: <45ED45B1.2030102@tastapod.com> Hi folks. So I'm using mocha on a ruby project, and I really like it. One thing I've noticed is that it can be a bit "surprising" when I'm mocking methods that don't exist on an object, and I think there's an easyish fix. At the moment, you use sheep = mock() to create a mock sheep. What I'd like to do is something like sheep = mock(Sheep), so the mock is typed. It's still duck-typed because the Sheep class is open, so I can set expectations on it like this: sheep.expects(:chew).with('grass') and then use the sheep somewhere. But here's the cool bit. When I invoke an expected method on the mock sheep (i.e. a method that matches an expectation), if the sheep is typed, it checks to see whether the type responds to the method (or if it implements method_missing). If neither of these is the case, it blows up like a regular sheep with a NoMethodError, rather than doing the expectation stuff. The reason for deferring the failure until the method is invoked rather than failing fast like JMock does when the expectation is defined, is in case the method gets defined, or the Sheep class gets extended, between the mock being defined and the method being invoked. Again, you would only get this behaviour if you created a typed mock, using sheep = mock(Sheep) rather than sheep = mock(). What do you think? Cheers, Dan ps. James: so that's where you got to! From dchelimsky at gmail.com Tue Mar 6 08:52:29 2007 From: dchelimsky at gmail.com (David Chelimsky) Date: Tue, 6 Mar 2007 07:52:29 -0600 Subject: [mocha-developer] mocking missing methods In-Reply-To: <45ED45B1.2030102@tastapod.com> References: <45ED45B1.2030102@tastapod.com> Message-ID: <57c63afe0703060552k7db574dcyac19aa74c7fff466@mail.gmail.com> On 3/6/07, Dan North wrote: > Hi folks. > > So I'm using mocha on a ruby project, and I really like it. One thing > I've noticed is that it can be a bit "surprising" when I'm mocking > methods that don't exist on an object, and I think there's an easyish fix. > > At the moment, you use sheep = mock() to create a mock sheep. What I'd > like to do is something like sheep = mock(Sheep), so the mock is typed. > It's still duck-typed because the Sheep class is open, so I can set > expectations on it like this: > > sheep.expects(:chew).with('grass') > > and then use the sheep somewhere. But here's the cool bit. > > When I invoke an expected method on the mock sheep (i.e. a method that > matches an expectation), if the sheep is typed, it checks to see whether > the type responds to the method (or if it implements method_missing). If > neither of these is the case, it blows up like a regular sheep with a > NoMethodError, rather than doing the expectation stuff. > > The reason for deferring the failure until the method is invoked rather > than failing fast like JMock does when the expectation is defined, is in > case the method gets defined, or the Sheep class gets extended, between > the mock being defined and the method being invoked. > > Again, you would only get this behaviour if you created a typed mock, > using sheep = mock(Sheep) rather than sheep = mock(). > > What do you think? We've got an open feature request for this in rspec as well, but with the ability to configure a test run to either report or not report these errors. The idea being that reporting that Sheep won't respond to certain messages can be a distraction from the work you're doing on Shepherd, but then when the task at hand reaches a good breaking point, you can run your tests w/ some flag that says "please tell me all the messages that I've mocked that I must now implement". I'd love to see this feature in mocha because it seems really useful in a test driven world, and because the day is not far off when rspec will support using mocha in addition to its own mock framework. The closer to parity of features we can get, the more likely people will be interested in using mocha within rspec. Cheers, David > > Cheers, > Dan > > ps. James: so that's where you got to! > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer > From jamesmead44 at gmail.com Wed Mar 7 05:14:48 2007 From: jamesmead44 at gmail.com (James Mead) Date: Wed, 7 Mar 2007 10:14:48 +0000 Subject: [mocha-developer] mocking missing methods In-Reply-To: <45ED45B1.2030102@tastapod.com> References: <45ED45B1.2030102@tastapod.com> Message-ID: <1db558f00703070214g10d9c286rf6e12562c6c8bda@mail.gmail.com> On 06/03/07, Dan North wrote: > > So I'm using mocha on a ruby project, and I really like it. One thing > I've noticed is that it can be a bit "surprising" when I'm mocking > methods that don't exist on an object, and I think there's an easyish fix. > > At the moment, you use sheep = mock() to create a mock sheep. What I'd > like to do is something like sheep = mock(Sheep), so the mock is typed. > It's still duck-typed because the Sheep class is open, so I can set > expectations on it like this: > > sheep.expects(:chew).with('grass') > > and then use the sheep somewhere. But here's the cool bit. > > When I invoke an expected method on the mock sheep (i.e. a method that > matches an expectation), if the sheep is typed, it checks to see whether > the type responds to the method (or if it implements method_missing). If > neither of these is the case, it blows up like a regular sheep with a > NoMethodError, rather than doing the expectation stuff. > > The reason for deferring the failure until the method is invoked rather > than failing fast like JMock does when the expectation is defined, is in > case the method gets defined, or the Sheep class gets extended, between > the mock being defined and the method being invoked. > > Again, you would only get this behaviour if you created a typed mock, > using sheep = mock(Sheep) rather than sheep = mock(). > > What do you think? > > Cheers, > Dan > > ps. James: so that's where you got to! > Hi Dan, Good to hear from you. Something very close to this idea has been on the to-do list for a long time, but I don't think we had thought of deferring the check to respond_to? until the actual moment the method on the mock is called. Good idea. Of course you'd be very welcome to send us a patch! My understanding of Ruby is that if you implement method_missing on a class, you should really implement respond_to? so that it reflects the methods that method_missing can handle. Is that your understanding? I know that not all Ruby libraries do this (notably ActiveRecord, at least when I last looked some months ago). Are you using Ruby in anger on a project? -- James. http://blog.floehopper.org From jamesmead44 at gmail.com Fri Mar 9 04:48:29 2007 From: jamesmead44 at gmail.com (James Mead) Date: Fri, 9 Mar 2007 09:48:29 +0000 Subject: [mocha-developer] Beyond multiple return values In-Reply-To: <7cfed280703051327v7f7b6405rbc42d423ff447c7c@mail.gmail.com> References: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> <7cfed280703051327v7f7b6405rbc42d423ff447c7c@mail.gmail.com> Message-ID: <1db558f00703090148x1a9336e5ucf92c38dfa62a727@mail.gmail.com> On 05/03/07, James Moore wrote: > > How about saying that if what is passed to returns() is an exception, or a > class that's a descendant of Exception, it's raised instead of returned? > You could still return an exception by returning it from a lambda. > > obj.stubs(:save!).returns(RuntimeError.new("testing..."), true) # Raises, > true > obj.stubs(:save!).returns(RuntimeError, true) # Raises, true > obj.stubs(:save!).returns(lambda {RuntimeError}, true) # returns the > exception object, true I'm not sure it's so clear what's going to happen in the above cases. Adding raises() to TestObject seems a bit intrusive. > In what way do you think it would be intrusive? -- James. http://blog.floehopper.org From jamesmead44 at gmail.com Fri Mar 9 04:49:56 2007 From: jamesmead44 at gmail.com (James Mead) Date: Fri, 9 Mar 2007 09:49:56 +0000 Subject: [mocha-developer] Fwd: Mocha raise exception first call, return value second call In-Reply-To: <1db558f00703070204l438029c5l9bb561139363c665@mail.gmail.com> References: <54ba4eb744c9efed001f03d26aec87d2@ruby-forum.com> <1db558f00703070204l438029c5l9bb561139363c665@mail.gmail.com> Message-ID: <1db558f00703090149q559cb06ck5636674af0de5003@mail.gmail.com> ---------- Forwarded message ---------- From: James Mead Date: 07-Mar-2007 10:04 Subject: Re: Mocha raise exception first call, return value second call To: ruby-talk at ruby-lang.org On 07/03/07, Raymond O'Connor wrote: > > Is there a way to have mocha raise an exception the first time an stub > is called, and then return a value the second time it is called? > > First example, I'm trying to connect to a server and test the case where > my first attempt raises a 'Server busy' exception or something of that > sort. So I wait, then retry and then the second time the connection is > successful. results = [ lambda { raise ServerBusy }, lambda { return true } ] object.stubs(:connect).returns { lambda { results.shift.call } } You might be interested in another recent thread (http://rubyforge.org/pipermail/mocha-developer/2007-March/000225.html ) which discusses how we might come up with a syntax to make this easier. -- James. http://blog.floehopper.org -- James. http://blog.floehopper.org From dan at tastapod.com Fri Mar 9 09:15:03 2007 From: dan at tastapod.com (Dan North) Date: Fri, 09 Mar 2007 14:15:03 +0000 Subject: [mocha-developer] Beyond multiple return values In-Reply-To: <1db558f00703090148x1a9336e5ucf92c38dfa62a727@mail.gmail.com> References: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> <7cfed280703051327v7f7b6405rbc42d423ff447c7c@mail.gmail.com> <1db558f00703090148x1a9336e5ucf92c38dfa62a727@mail.gmail.com> Message-ID: <45F16BE7.1050908@tastapod.com> I have to agree with James - I'm not a fan of returns changing its behaviour based on the return type. What if I'm writing an exceptions factory, or some sort of exception processor (say something to log parts of a stack trace)? I'm happy with .returns(quietly) and .raises(mayhem). Cheers, Dan James Mead wrote: > On 05/03/07, James Moore wrote: > >> How about saying that if what is passed to returns() is an exception, or a >> class that's a descendant of Exception, it's raised instead of returned? >> You could still return an exception by returning it from a lambda. >> >> obj.stubs(:save!).returns(RuntimeError.new("testing..."), true) # Raises, >> true >> obj.stubs(:save!).returns(RuntimeError, true) # Raises, true >> obj.stubs(:save!).returns(lambda {RuntimeError}, true) # returns the >> exception object, true >> > > > I'm not sure it's so clear what's going to happen in the above cases. > > Adding raises() to TestObject seems a bit intrusive. > > > In what way do you think it would be intrusive? > > From jamesthepiper at gmail.com Fri Mar 9 18:38:53 2007 From: jamesthepiper at gmail.com (James Moore) Date: Fri, 9 Mar 2007 15:38:53 -0800 Subject: [mocha-developer] Beyond multiple return values In-Reply-To: <45F16BE7.1050908@tastapod.com> References: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> <7cfed280703051327v7f7b6405rbc42d423ff447c7c@mail.gmail.com> <1db558f00703090148x1a9336e5ucf92c38dfa62a727@mail.gmail.com> <45F16BE7.1050908@tastapod.com> Message-ID: <7cfed280703091538u7c39893cxb03c4068332babe0@mail.gmail.com> On 3/9/07, Dan North wrote: > > I have to agree with James - I'm not a fan of returns changing its > behaviour based on the return type. What if I'm writing an exceptions > factory, or some sort of exception processor (say something to log parts > of a stack trace)? > > I'm happy with .returns(quietly) and .raises(mayhem). > It does already, though. For example, there's not a clean way to mock something that returns closures, which is probably more common than something that returns exceptions. If returns() is given something that happens to be a lambda, it doesn't return the lambda (which is what you'd normally expect), it calls it and returns its value instead. I'm just suggesting adding to that concept: case return_value when its a Proc: call the proc, return the result # Existing behavior when its an Exception, or an instance of an exception, raise it # Additional behavior else return return_value end I do see your point though - I'm not convinced this is worth arguing strongly for, but I do think it fits well with the current code. - James Moore From dan at tastapod.com Sat Mar 10 07:45:04 2007 From: dan at tastapod.com (Dan North) Date: Sat, 10 Mar 2007 12:45:04 +0000 Subject: [mocha-developer] Beyond multiple return values In-Reply-To: <7cfed280703091538u7c39893cxb03c4068332babe0@mail.gmail.com> References: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> <7cfed280703051327v7f7b6405rbc42d423ff447c7c@mail.gmail.com> <1db558f00703090148x1a9336e5ucf92c38dfa62a727@mail.gmail.com> <45F16BE7.1050908@tastapod.com> <7cfed280703091538u7c39893cxb03c4068332babe0@mail.gmail.com> Message-ID: <45F2A850.5030304@tastapod.com> Ah, that's "surprising". I haven't tried using lambdas with mocha, but that's not what I would expect. I expect returns to, um, return stuff. I would expect to use raises and executes (or maybe invokes) for exceptions and lambdas respectively. returns(&block) actually executing the block seems counter-intuitive to me. sheep.expects(:eat).with(grass).*executes*(proc {puts "chewing"}) proc_factory.expects(:create).*returns*(proc { puts "pretend the factory created this" }) Come to think of it, is there any way in mocha of doing the second one? Cheers, Dan James Moore wrote: > On 3/9/07, Dan North wrote: > >> I have to agree with James - I'm not a fan of returns changing its >> behaviour based on the return type. What if I'm writing an exceptions >> factory, or some sort of exception processor (say something to log parts >> of a stack trace)? >> >> I'm happy with .returns(quietly) and .raises(mayhem). >> >> > > It does already, though. For example, there's not a clean way to mock > something that returns closures, which is probably more common than > something that returns exceptions. If returns() is given something that > happens to be a lambda, it doesn't return the lambda (which is what you'd > normally expect), it calls it and returns its value instead. I'm just > suggesting adding to that concept: > > case return_value > when its a Proc: call the proc, return the result # Existing behavior > when its an Exception, or an instance of an exception, raise it # Additional > behavior > else return return_value > end > > I do see your point though - I'm not convinced this is worth arguing > strongly for, but I do think it fits well with the current code. > > - James Moore > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer > From jamesthepiper at gmail.com Sun Mar 11 22:07:04 2007 From: jamesthepiper at gmail.com (James Moore) Date: Sun, 11 Mar 2007 19:07:04 -0700 Subject: [mocha-developer] Beyond multiple return values In-Reply-To: <1db558f00703090148x1a9336e5ucf92c38dfa62a727@mail.gmail.com> References: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> <7cfed280703051327v7f7b6405rbc42d423ff447c7c@mail.gmail.com> <1db558f00703090148x1a9336e5ucf92c38dfa62a727@mail.gmail.com> Message-ID: <7cfed280703111907x542bc92fy4235fb24f0b4a7a8@mail.gmail.com> On 3/9/07, James Mead wrote: > > Adding raises() to TestObject seems a bit intrusive. > > > > In what way do you think it would be intrusive? > > -- > James. > http://blog.floehopper.org > It's adding a method to another, basically unrelated, class object just to change the readability of mocks. It seems goofy to add a method to a class where the new method has little or no interaction with the class it belongs to. - James Moore From jamesmead44 at gmail.com Mon Mar 12 06:43:59 2007 From: jamesmead44 at gmail.com (James Mead) Date: Mon, 12 Mar 2007 10:43:59 +0000 Subject: [mocha-developer] Beyond multiple return values In-Reply-To: <45F2A850.5030304@tastapod.com> References: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> <7cfed280703051327v7f7b6405rbc42d423ff447c7c@mail.gmail.com> <1db558f00703090148x1a9336e5ucf92c38dfa62a727@mail.gmail.com> <45F16BE7.1050908@tastapod.com> <7cfed280703091538u7c39893cxb03c4068332babe0@mail.gmail.com> <45F2A850.5030304@tastapod.com> Message-ID: <1db558f00703120343w48c61690xbc81564855bb3823@mail.gmail.com> On 10/03/07, Dan North wrote: > > Ah, that's "surprising". I haven't tried using lambdas with mocha, but > that's not what I would expect. I expect returns to, um, return stuff. I > would expect to use raises and executes (or maybe invokes) for > exceptions and lambdas respectively. > > returns(&block) actually executing the block seems counter-intuitive to > me. > > sheep.expects(:eat).with(grass).*executes*(proc {puts "chewing"}) > > proc_factory.expects(:create).*returns*(proc { puts "pretend the factory > created this" }) > > Come to think of it, is there any way in mocha of doing the second one? > > Cheers, > Dan > > James Moore wrote: > > On 3/9/07, Dan North wrote: > > > >> I have to agree with James - I'm not a fan of returns changing its > >> behaviour based on the return type. What if I'm writing an exceptions > >> factory, or some sort of exception processor (say something to log > parts > >> of a stack trace)? > >> > >> I'm happy with .returns(quietly) and .raises(mayhem). > >> > >> > > > > It does already, though. For example, there's not a clean way to mock > > something that returns closures, which is probably more common than > > something that returns exceptions. If returns() is given something that > > happens to be a lambda, it doesn't return the lambda (which is what > you'd > > normally expect), it calls it and returns its value instead. I'm just > > suggesting adding to that concept: > > > > case return_value > > when its a Proc: call the proc, return the result # Existing behavior > > when its an Exception, or an instance of an exception, raise it # > Additional > > behavior > > else return return_value > > end > > > > I do see your point though - I'm not convinced this is worth arguing > > strongly for, but I do think it fits well with the current code. > > > > - James Moore > James & Dan - you both make very valid points. Thanks. I'd like to make the API less surprising. Surprising is BAD! Specifically I'd like to make it possible to return exactly what is specified in the 'returns' method, even if that is a Proc instance. My colleague Chris pointed out that there is a similar issue in the 'with' method which can accept a block. I'd like to fix that too. My colleagues and I have been chucking a few ideas around. I'll try and post about these ideas as soon as I have some time. One of the issues is providing a simple migration path for those with lots of tests relying on the current behaviour. I'd be interested whether you have any thoughts. -- James. http://blog.floehopper.org From jamesmead44 at gmail.com Mon Mar 12 06:46:00 2007 From: jamesmead44 at gmail.com (James Mead) Date: Mon, 12 Mar 2007 10:46:00 +0000 Subject: [mocha-developer] Beyond multiple return values In-Reply-To: <7cfed280703111907x542bc92fy4235fb24f0b4a7a8@mail.gmail.com> References: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> <7cfed280703051327v7f7b6405rbc42d423ff447c7c@mail.gmail.com> <1db558f00703090148x1a9336e5ucf92c38dfa62a727@mail.gmail.com> <7cfed280703111907x542bc92fy4235fb24f0b4a7a8@mail.gmail.com> Message-ID: <1db558f00703120346w3e1c0f6dma71b67c1456fa13b@mail.gmail.com> On 12/03/07, James Moore wrote: > > On 3/9/07, James Mead wrote: > > > > Adding raises() to TestObject seems a bit intrusive. > > > > > > > In what way do you think it would be intrusive? > > It's adding a method to another, basically unrelated, class object just to > change the readability of mocks. It seems goofy to add a method to a > class > where the new method has little or no interaction with the class it > belongs > to. > > - James Moore > I agree it's not very object-oriented, but I'd be happy to sacrifice this for readability. Readability is very important to me. Thanks for responding - it helps to know why you think it would be intrusive. -- James. http://blog.floehopper.org From dan at tastapod.com Mon Mar 12 17:17:37 2007 From: dan at tastapod.com (Dan North) Date: Mon, 12 Mar 2007 21:17:37 +0000 Subject: [mocha-developer] using mocha with rspec Message-ID: <45F5C371.9050702@tastapod.com> Hi folks. I've just started using rspec and I have to say it's very nice. The thing is, I prefer mocha's mocking dialect. So I thought a simple require 'mocha' would set me up. Unfortunately, rspec does all its goodness using do/end blocks in anonymous classes, so it wasn't quite that obvious. Anyway, here is the incantation I ended up using in my equivalent of a test_helper.rb file that I include in all my spec files: # replace rspec's mocks with mocha require 'mocha' module Spec module Mocks remove_method :mock remove_method :stub remove_method :verify_mocks if method_defined? :verify_mocks include Mocha::AutoVerify end end Now I have working mock(), stub() and verify_mocks(). Not sure what other Mocha stuff I'm missing yet, but I'll let you know how I get on. I noticed that Mocha::Standalone also references Mocha::SetupAndTeardown, but I didn't see anything in there that seemed relevant to rspec mocking. Cheers, Dan From dan at tastapod.com Mon Mar 12 17:58:18 2007 From: dan at tastapod.com (Dan North) Date: Mon, 12 Mar 2007 21:58:18 +0000 Subject: [mocha-developer] expectations in order Message-ID: <45F5CCFA.5040801@tastapod.com> Hi folks. Does mocha support expectations in a sequence, like JMock's after(...) method? Or something like that to support ordered expectations? Thanks, Dan From jamesthepiper at gmail.com Mon Mar 12 19:47:07 2007 From: jamesthepiper at gmail.com (James Moore) Date: Mon, 12 Mar 2007 16:47:07 -0700 Subject: [mocha-developer] Beyond multiple return values In-Reply-To: <1db558f00703120346w3e1c0f6dma71b67c1456fa13b@mail.gmail.com> References: <94D7790D-5D28-4FB5-A5CF-6FF9E866B3E2@82ask.com> <1db558f00703050306p72a1acd5v9cd0e2337255f70f@mail.gmail.com> <7cfed280703051327v7f7b6405rbc42d423ff447c7c@mail.gmail.com> <1db558f00703090148x1a9336e5ucf92c38dfa62a727@mail.gmail.com> <7cfed280703111907x542bc92fy4235fb24f0b4a7a8@mail.gmail.com> <1db558f00703120346w3e1c0f6dma71b67c1456fa13b@mail.gmail.com> Message-ID: <7cfed280703121647j49721a19q233ff067d9f18bf5@mail.gmail.com> On 3/12/07, James Mead wrote: > > I agree it's not very object-oriented, but I'd be happy to sacrifice this > for readability. Readability is very important to me. > > Thanks for responding - it helps to know why you think it would be > intrusive. How about this: it's not predictable. I think what you're saying is that you'd like to add Test::Unit::TestCase#raises, but that only works when you're creating stuff inside a test method. Does that mean creating return values outside of a test is going to look very different than creating them inside a test? It also seems vulnerable to namespace clashes - seems like if you have a good argument for adding Test::Unit::TestCase#raises, so can someone else, for some other test-related library, and they're not going to play well together. (I'm not wildly opposed to this, it's just feeling like there's a better way somewhere.) - James Moore From jamesmead44 at gmail.com Tue Mar 13 05:26:22 2007 From: jamesmead44 at gmail.com (James Mead) Date: Tue, 13 Mar 2007 09:26:22 +0000 Subject: [mocha-developer] using mocha with rspec In-Reply-To: <45F5C371.9050702@tastapod.com> References: <45F5C371.9050702@tastapod.com> Message-ID: <1db558f00703130226h7a2b7c4cvc469be9cdb20e40c@mail.gmail.com> On 12/03/07, Dan North wrote: > > I've just started using rspec and I have to say it's very nice. The > thing is, I prefer mocha's mocking dialect. So I thought a simple > require 'mocha' would set me up. > > Unfortunately, rspec does all its goodness using do/end blocks in > anonymous classes, so it wasn't quite that obvious. > > Anyway, here is the incantation I ended up using in my equivalent of a > test_helper.rb file that I include in all my spec files: > > # replace rspec's mocks with mocha > require 'mocha' > module Spec > module Mocks > remove_method :mock > remove_method :stub > remove_method :verify_mocks if method_defined? :verify_mocks > include Mocha::AutoVerify > end > end > > Now I have working mock(), stub() and verify_mocks(). Not sure what > other Mocha stuff I'm missing yet, but I'll let you know how I get on. > > I noticed that Mocha::Standalone also references > Mocha::SetupAndTeardown, but I didn't see anything in there that seemed > relevant to rspec mocking. Hi Dan, I'm glad you like Mocha's mocking dialect. It's great you've got Mocha working with RSpec. Mocha::Standalone was an early attempt at simplifying integration with other testing frameworks prompted by a request from the RSpec community. Work on this ground to a halt when RSpec developed their own mocking functionality. Mocha::SetupAndTeardown is to do with what RSpec call partial mocks i.e. where you are mocking a method on a concrete class or object. Looking at what you've done, I suspect you will still be able to setup expectations like this but (a) they won't be verified and (b) the concrete class or object won't be returned to its original state (Mocha temporarily messes with the method implementation). Let us know if there are any changes we can make that would make the integration easier. -- James. http://blog.floehopper.org From jamesmead44 at gmail.com Tue Mar 13 05:48:26 2007 From: jamesmead44 at gmail.com (James Mead) Date: Tue, 13 Mar 2007 09:48:26 +0000 Subject: [mocha-developer] expectations in order In-Reply-To: <45F5CCFA.5040801@tastapod.com> References: <45F5CCFA.5040801@tastapod.com> Message-ID: <1db558f00703130248k5630263bt49af12229cfa3d1d@mail.gmail.com> On 12/03/07, Dan North wrote: > > Does mocha support expectations in a sequence, like JMock's after(...) > method? Or something like that to support ordered expectations? > Not yet - it's another of the many things on the todo list. Patches welcome ;-) -- James. http://blog.floehopper.org From dan at tastapod.com Tue Mar 13 07:25:50 2007 From: dan at tastapod.com (Dan North) Date: Tue, 13 Mar 2007 11:25:50 +0000 Subject: [mocha-developer] using mocha with rspec In-Reply-To: <1db558f00703130226h7a2b7c4cvc469be9cdb20e40c@mail.gmail.com> References: <45F5C371.9050702@tastapod.com> <1db558f00703130226h7a2b7c4cvc469be9cdb20e40c@mail.gmail.com> Message-ID: <45F68A3E.7040505@tastapod.com> Yes you're right - it doesn't automagically verify, but the verify_mocks method is in scope and I like to have that explicitly in my spec methods anyway. The big things on my wishlist are: 1. Matchers in with(..) like JMock has: expects(:chew).with(is_a(Chewable), ANYTHING) where is_a(..) is a [something] that responds to matches?(actual) with true/false, and ANYTHING is a matcher that always returns true. That way, you could use rspec's expectation matcher framework or roll your own matchers. (I've got a bunch I wrote for my own mocking framework - everyone should write a mocking framework at least twice once :) The method matches?(actual) seems fairly universal across these frameworks. 2. Type-aware mocks that fail with a NoMethodError if the underlying type doesn't respond to an invoked method or method_missing (see earlier thread). 3. Something akin to after(another_mock, :method). None of these are showstoppers, but 1 is really high on my priorities and 2 and 3 would make my life happier. I might try to roll something for 1 if I get the time (read: unlikely!). Cheers, Dan James Mead wrote: > On 12/03/07, Dan North wrote: > >> I've just started using rspec and I have to say it's very nice. The >> thing is, I prefer mocha's mocking dialect. So I thought a simple >> require 'mocha' would set me up. >> >> Unfortunately, rspec does all its goodness using do/end blocks in >> anonymous classes, so it wasn't quite that obvious. >> >> Anyway, here is the incantation I ended up using in my equivalent of a >> test_helper.rb file that I include in all my spec files: >> >> # replace rspec's mocks with mocha >> require 'mocha' >> module Spec >> module Mocks >> remove_method :mock >> remove_method :stub >> remove_method :verify_mocks if method_defined? :verify_mocks >> include Mocha::AutoVerify >> end >> end >> >> Now I have working mock(), stub() and verify_mocks(). Not sure what >> other Mocha stuff I'm missing yet, but I'll let you know how I get on. >> >> I noticed that Mocha::Standalone also references >> Mocha::SetupAndTeardown, but I didn't see anything in there that seemed >> relevant to rspec mocking. >> > > > Hi Dan, > > I'm glad you like Mocha's mocking dialect. It's great you've got Mocha > working with RSpec. > > Mocha::Standalone was an early attempt at simplifying integration with other > testing frameworks prompted by a request from the RSpec community. Work on > this ground to a halt when RSpec developed their own mocking functionality. > > Mocha::SetupAndTeardown is to do with what RSpec call partial mocks i.e. > where you are mocking a method on a concrete class or object. Looking at > what you've done, I suspect you will still be able to setup expectations > like this but (a) they won't be verified and (b) the concrete class or > object won't be returned to its original state (Mocha temporarily messes > with the method implementation). > > Let us know if there are any changes we can make that would make the > integration easier. > From dchelimsky at gmail.com Tue Mar 13 07:47:33 2007 From: dchelimsky at gmail.com (David Chelimsky) Date: Tue, 13 Mar 2007 06:47:33 -0500 Subject: [mocha-developer] using mocha with rspec In-Reply-To: <1db558f00703130226h7a2b7c4cvc469be9cdb20e40c@mail.gmail.com> References: <45F5C371.9050702@tastapod.com> <1db558f00703130226h7a2b7c4cvc469be9cdb20e40c@mail.gmail.com> Message-ID: <57c63afe0703130447q1bc29f40k9a1b5e63f56827ec@mail.gmail.com> On 3/13/07, James Mead wrote: > On 12/03/07, Dan North wrote: > > > > I've just started using rspec and I have to say it's very nice. The > > thing is, I prefer mocha's mocking dialect. So I thought a simple > > require 'mocha' would set me up. > > > > Unfortunately, rspec does all its goodness using do/end blocks in > > anonymous classes, so it wasn't quite that obvious. > > > > Anyway, here is the incantation I ended up using in my equivalent of a > > test_helper.rb file that I include in all my spec files: > > > > # replace rspec's mocks with mocha > > require 'mocha' > > module Spec > > module Mocks > > remove_method :mock > > remove_method :stub > > remove_method :verify_mocks if method_defined? :verify_mocks > > include Mocha::AutoVerify > > end > > end > > > > Now I have working mock(), stub() and verify_mocks(). Not sure what > > other Mocha stuff I'm missing yet, but I'll let you know how I get on. > > > > I noticed that Mocha::Standalone also references > > Mocha::SetupAndTeardown, but I didn't see anything in there that seemed > > relevant to rspec mocking. > > > Hi Dan, > > I'm glad you like Mocha's mocking dialect. It's great you've got Mocha > working with RSpec. > > Mocha::Standalone was an early attempt at simplifying integration with other > testing frameworks prompted by a request from the RSpec community. Work on > this ground to a halt when RSpec developed their own mocking functionality. > > Mocha::SetupAndTeardown is to do with what RSpec call partial mocks i.e. > where you are mocking a method on a concrete class or object. Looking at > what you've done, I suspect you will still be able to setup expectations > like this but (a) they won't be verified and (b) the concrete class or > object won't be returned to its original state (Mocha temporarily messes > with the method implementation). > > Let us know if there are any changes we can make that would make the > integration easier. Dan and James, Thank you both for moving on this. I've been wanting to figure out an easy way to let people choose their mock framework when they use rspec, but there have been other priorities in rspec and limited resources. What I'd like to see is something that I can build into rspec such that people can either choose to use mocha for an entire project by putting a single declaration in a helper file with no monkey patching (in fact, the structure Dan patched has already changed in rspec's trunk), or by including a module in a given context/describe block. If you guys can work together to make that happen, I'll make it a first class citizen of rspec as soon as any relevant changes are released in mocha. Cheers, David > -- > James. > http://blog.floehopper.org > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer > From jamesmead44 at gmail.com Tue Mar 13 07:53:44 2007 From: jamesmead44 at gmail.com (James Mead) Date: Tue, 13 Mar 2007 11:53:44 +0000 Subject: [mocha-developer] using mocha with rspec In-Reply-To: <45F68A3E.7040505@tastapod.com> References: <45F5C371.9050702@tastapod.com> <1db558f00703130226h7a2b7c4cvc469be9cdb20e40c@mail.gmail.com> <45F68A3E.7040505@tastapod.com> Message-ID: <1db558f00703130453n7b1ae77cxb07e459a4c4a980b@mail.gmail.com> On 13/03/07, Dan North wrote: > > Yes you're right - it doesn't automagically verify, but the verify_mocks > method is in scope and I like to have that explicitly in my spec methods > anyway. Actually I was thinking of the poorly named "verify_stubs" (as opposed to "verify_mocks") and "teardown_stubs" methods in Mocha::SetupAndTeardown. These are to do with the "partial mocking" I mentioned in my previous email. But perhaps you aren't using that aspect of Mocha. If you do something like this... now = Time.parse('2006-01-01 00:00:00') Time.stubs(:now).returns(now) Unless you call the methods in Mocha::SetupAndTeardown, "Time.now" will continue to return the fixed value "now" after the test has finished. -- James. http://blog.floehopper.org From dchelimsky at gmail.com Tue Mar 13 07:55:13 2007 From: dchelimsky at gmail.com (David Chelimsky) Date: Tue, 13 Mar 2007 06:55:13 -0500 Subject: [mocha-developer] using mocha with rspec In-Reply-To: <45F68A3E.7040505@tastapod.com> References: <45F5C371.9050702@tastapod.com> <1db558f00703130226h7a2b7c4cvc469be9cdb20e40c@mail.gmail.com> <45F68A3E.7040505@tastapod.com> Message-ID: <57c63afe0703130455m7b31a19am71f3e85ec47110c9@mail.gmail.com> On 3/13/07, Dan North wrote: > Yes you're right - it doesn't automagically verify, but the verify_mocks > method is in scope and I like to have that explicitly in my spec methods > anyway. > > The big things on my wishlist are: > > 1. Matchers in with(..) like JMock has: > expects(:chew).with(is_a(Chewable), ANYTHING) where is_a(..) is a > [something] that responds to matches?(actual) with true/false, and > ANYTHING is a matcher that always returns true. That way, you could use > rspec's expectation matcher framework or roll your own matchers. (I've > got a bunch I wrote for my own mocking framework - everyone should write > a mocking framework at least twice once :) > > The method matches?(actual) seems fairly universal across these frameworks. rspec's mocking framework uses matcher.matches?(actual) and use matcher.description to build the failure message. If you use these methods, then rspec users will be able to use rspec's matchers (which are usable for both mock expectations and state expectations/assertions) within mocha expectations in rspec. In fact, test/unit users would be able to use them as well - they'd just have to get the rspec gem and require 'spec/matchers' (which doesn't depend on any other parts of rspec). Hope I'm not being too intrusive here. I'd really love to see the day where rspec need not support its own mocking framework. Making mocha a first class option within rspec is a big first step towards this goal. > > 2. Type-aware mocks that fail with a NoMethodError if the underlying > type doesn't respond to an invoked method or method_missing (see earlier > thread). > > 3. Something akin to after(another_mock, :method). > > None of these are showstoppers, but 1 is really high on my priorities > and 2 and 3 would make my life happier. I might try to roll something > for 1 if I get the time (read: unlikely!). > > Cheers, > Dan > > James Mead wrote: > > On 12/03/07, Dan North wrote: > > > >> I've just started using rspec and I have to say it's very nice. The > >> thing is, I prefer mocha's mocking dialect. So I thought a simple > >> require 'mocha' would set me up. > >> > >> Unfortunately, rspec does all its goodness using do/end blocks in > >> anonymous classes, so it wasn't quite that obvious. > >> > >> Anyway, here is the incantation I ended up using in my equivalent of a > >> test_helper.rb file that I include in all my spec files: > >> > >> # replace rspec's mocks with mocha > >> require 'mocha' > >> module Spec > >> module Mocks > >> remove_method :mock > >> remove_method :stub > >> remove_method :verify_mocks if method_defined? :verify_mocks > >> include Mocha::AutoVerify > >> end > >> end > >> > >> Now I have working mock(), stub() and verify_mocks(). Not sure what > >> other Mocha stuff I'm missing yet, but I'll let you know how I get on. > >> > >> I noticed that Mocha::Standalone also references > >> Mocha::SetupAndTeardown, but I didn't see anything in there that seemed > >> relevant to rspec mocking. > >> > > > > > > Hi Dan, > > > > I'm glad you like Mocha's mocking dialect. It's great you've got Mocha > > working with RSpec. > > > > Mocha::Standalone was an early attempt at simplifying integration with other > > testing frameworks prompted by a request from the RSpec community. Work on > > this ground to a halt when RSpec developed their own mocking functionality. > > > > Mocha::SetupAndTeardown is to do with what RSpec call partial mocks i.e. > > where you are mocking a method on a concrete class or object. Looking at > > what you've done, I suspect you will still be able to setup expectations > > like this but (a) they won't be verified and (b) the concrete class or > > object won't be returned to its original state (Mocha temporarily messes > > with the method implementation). > > > > Let us know if there are any changes we can make that would make the > > integration easier. > > > > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer > From jamesmead44 at gmail.com Tue Mar 13 08:03:34 2007 From: jamesmead44 at gmail.com (James Mead) Date: Tue, 13 Mar 2007 12:03:34 +0000 Subject: [mocha-developer] using mocha with rspec In-Reply-To: <45F68A3E.7040505@tastapod.com> References: <45F5C371.9050702@tastapod.com> <1db558f00703130226h7a2b7c4cvc469be9cdb20e40c@mail.gmail.com> <45F68A3E.7040505@tastapod.com> Message-ID: <1db558f00703130503i19c9ab04l569e62248d7d4eb2@mail.gmail.com> On 13/03/07, Dan North wrote: > > The big things on my wishlist are: > > 1. Matchers in with(..) like JMock has: > expects(:chew).with(is_a(Chewable), ANYTHING) where is_a(..) is a > [something] that responds to matches?(actual) with true/false, and > ANYTHING is a matcher that always returns true. That way, you could use > rspec's expectation matcher framework or roll your own matchers. (I've > got a bunch I wrote for my own mocking framework - everyone should write > a mocking framework at least twice once :) > > The method matches?(actual) seems fairly universal across these > frameworks. Something like this has been on the drawing board for a while. You can currently achieve something similar by using a block on the call to with(), but I admit the syntax is not great. expects(:chew).with { |mouthful1, mouthful2| mouthful1.is_a?(Chewable) } 2. Type-aware mocks that fail with a NoMethodError if the underlying > type doesn't respond to an invoked method or method_missing (see earlier > thread). 3. Something akin to after(another_mock, :method). > > None of these are showstoppers, but 1 is really high on my priorities > and 2 and 3 would make my life happier. I might try to roll something > for 1 if I get the time (read: unlikely!). > Cool. My bandwidth is a bit limited at the moment too, but I'll see what we can do. Thanks for your constructive suggestions. -- James. http://blog.floehopper.org From dan at tastapod.com Tue Mar 13 08:41:15 2007 From: dan at tastapod.com (Dan North) Date: Tue, 13 Mar 2007 12:41:15 +0000 Subject: [mocha-developer] using mocha with rspec In-Reply-To: <1db558f00703130503i19c9ab04l569e62248d7d4eb2@mail.gmail.com> References: <45F5C371.9050702@tastapod.com> <1db558f00703130226h7a2b7c4cvc469be9cdb20e40c@mail.gmail.com> <45F68A3E.7040505@tastapod.com> <1db558f00703130503i19c9ab04l569e62248d7d4eb2@mail.gmail.com> Message-ID: <45F69BEB.1040006@tastapod.com> I came up with a hacky way of doing named matchers by using lambdas: first_arg_is_chewable = proc {|arg1, arg2| arg1.is_a? Chewable } # ... sheep.expects(:chew).with(&first_arg_is_chewable) which while readable is fugly :) I used it when I wanted to reuse the same with-block. But yes, positional matchers would be much easier on both the eye and the brain! James Mead wrote: > On 13/03/07, Dan North wrote: > >> The big things on my wishlist are: >> >> 1. Matchers in with(..) like JMock has: >> expects(:chew).with(is_a(Chewable), ANYTHING) where is_a(..) is a >> [something] that responds to matches?(actual) with true/false, and >> ANYTHING is a matcher that always returns true. That way, you could use >> rspec's expectation matcher framework or roll your own matchers. (I've >> got a bunch I wrote for my own mocking framework - everyone should write >> a mocking framework at least twice once :) >> >> The method matches?(actual) seems fairly universal across these >> frameworks. >> > > > Something like this has been on the drawing board for a while. > > You can currently achieve something similar by using a block on the call to > with(), but I admit the syntax is not great. > > expects(:chew).with { |mouthful1, mouthful2| mouthful1.is_a?(Chewable) } > > 2. Type-aware mocks that fail with a NoMethodError if the underlying > >> type doesn't respond to an invoked method or method_missing (see earlier >> thread). >> > > > > 3. Something akin to after(another_mock, :method). > >> None of these are showstoppers, but 1 is really high on my priorities >> and 2 and 3 would make my life happier. I might try to roll something >> for 1 if I get the time (read: unlikely!). >> >> > > Cool. My bandwidth is a bit limited at the moment too, but I'll see what we > can do. > > Thanks for your constructive suggestions. > > From jamesmead44 at gmail.com Wed Mar 14 11:23:37 2007 From: jamesmead44 at gmail.com (James Mead) Date: Wed, 14 Mar 2007 15:23:37 +0000 Subject: [mocha-developer] using mocha with rspec In-Reply-To: <45F69BEB.1040006@tastapod.com> References: <45F5C371.9050702@tastapod.com> <1db558f00703130226h7a2b7c4cvc469be9cdb20e40c@mail.gmail.com> <45F68A3E.7040505@tastapod.com> <1db558f00703130503i19c9ab04l569e62248d7d4eb2@mail.gmail.com> <45F69BEB.1040006@tastapod.com> Message-ID: <1db558f00703140823j13b1074aq9b6b70695e8701d3@mail.gmail.com> The other thing I should have suggested is - have a look at standalone_acceptance_test.rb and look at the NotATestUnitTestCase class. -- James. http://blog.floehopper.org From rainer.jung at gmail.com Thu Mar 15 14:36:15 2007 From: rainer.jung at gmail.com (Rainer Jung) Date: Thu, 15 Mar 2007 19:36:15 +0100 Subject: [mocha-developer] Howto find out, a function was called Message-ID: <14ae67820703151136n1c9fcfe0v14458c4ceeb1455b@mail.gmail.com> Hello there! I just started with Mocha (great job, by the way), and want to solve a problem, perhaps you can give me a hint. I have a Observer (yes, in Rails), and want to test, that this observer does (or does not) call a special method on the observed object. In this example, the object has a sync-method, that shall be called before_update. The Observer looks like class BlaObserver < Observer @@sync = true def before_save(object) object.sync if @@sync end def self.ignore before = @@sync @@sync = false yield @@sync = before end end Now i want to test, if the method sync is called, if i call BlaObserver.ignore do object.save end Do you have an idea? Regards, Rainer From jamesmead44 at gmail.com Thu Mar 15 15:33:06 2007 From: jamesmead44 at gmail.com (James Mead) Date: Thu, 15 Mar 2007 19:33:06 +0000 Subject: [mocha-developer] Howto find out, a function was called In-Reply-To: <14ae67820703151136n1c9fcfe0v14458c4ceeb1455b@mail.gmail.com> References: <14ae67820703151136n1c9fcfe0v14458c4ceeb1455b@mail.gmail.com> Message-ID: <1db558f00703151233j6c217b3aoa87ee452e5f4bf58@mail.gmail.com> I'm not entirely sure I follow your code, but can you not do something like... def test_me object.expects(:sync) BlaObserver.ignore do object.save end end or def test_me object.expects(:sync).never BlaObserver.ignore do object.save end end -- James. http://blog.floehopper.org From jay at jayfields.com Sun Mar 18 09:17:43 2007 From: jay at jayfields.com (Jay Fields) Date: Sun, 18 Mar 2007 09:17:43 -0400 Subject: [mocha-developer] using mocha with rspec In-Reply-To: <45F69BEB.1040006@tastapod.com> References: <45F5C371.9050702@tastapod.com> <1db558f00703130226h7a2b7c4cvc469be9cdb20e40c@mail.gmail.com> <45F68A3E.7040505@tastapod.com> <1db558f00703130503i19c9ab04l569e62248d7d4eb2@mail.gmail.com> <45F69BEB.1040006@tastapod.com> Message-ID: <02747787-F41E-4728-B8B8-D7AC04E4DB5E@jayfields.com> Hello Dan & James The code below should provide the feature you are looking for. It allows you to create an expectation like this: obj.expects(:a_method).with(1, Fixnum, Object) obj.a_method(1, 100, :anything) Index: trunk/test/unit/expectation_test.rb =================================================================== --- trunk/test/unit/expectation_test.rb (revision 109) +++ trunk/test/unit/expectation_test.rb (working copy) @@ -30,6 +30,16 @@ assert expectation.match?(:expected_method, 1, 2, 3) end + def test_should_match_calls_to_same_method_with_expected_parameter_values_or _class + expectation = new_expectation.with(1, Fixnum, Object) + assert expectation.match?(:expected_method, 1, 2, 3) + end + + def test_should_match_calls_to_same_method_with_expected_parameter_values_or _class + expectation = new_expectation.with(1, Fixnum, Object) + assert expectation.match?(:expected_method, 1, Fixnum, 3) + end + def test_should_match_calls_to_same_method_with_parameters_constrained_as_ex pected expectation = new_expectation.with() {|x, y, z| x + y == z} assert expectation.match?(:expected_method, 1, 2, 3) Index: trunk/lib/mocha/expectation.rb =================================================================== --- trunk/lib/mocha/expectation.rb (revision 109) +++ trunk/lib/mocha/expectation.rb (working copy) @@ -22,9 +22,6 @@ class InvalidExpectation < Exception; end class AlwaysEqual - def ==(other) - true - end end attr_reader :method_name, :backtrace @@ -43,7 +40,15 @@ end def match?(method_name, *arguments) - (@method_name == method_name) and (@parameter_block ? @parameter_block.call(*arguments) : (@parameters == arguments)) + return false unless @method_name == method_name + return @parameter_block.call(*arguments) unless @parameter_block.nil? + return true if @parameters.is_a? AlwaysEqual + return false unless @parameters.size == arguments.size + @parameters.inject([0, true]) do |result, arg| + result[1] &&= (arguments[result.first].is_a?(Module) ? arg == arguments[result.first] : arg === arguments[result.first]) + result[0] += 1 + result + end.last end # :startdoc: On Mar 13, 2007, at 8:41 AM, Dan North wrote: > I came up with a hacky way of doing named matchers by using lambdas: > > first_arg_is_chewable = proc {|arg1, arg2| arg1.is_a? Chewable } > # ... > sheep.expects(:chew).with(&first_arg_is_chewable) > > which while readable is fugly :) I used it when I wanted to reuse the > same with-block. > > But yes, positional matchers would be much easier on both the eye and > the brain! > > James Mead wrote: >> On 13/03/07, Dan North wrote: >> >>> The big things on my wishlist are: >>> >>> 1. Matchers in with(..) like JMock has: >>> expects(:chew).with(is_a(Chewable), ANYTHING) where is_a(..) is a >>> [something] that responds to matches?(actual) with true/false, and >>> ANYTHING is a matcher that always returns true. That way, you >>> could use >>> rspec's expectation matcher framework or roll your own matchers. >>> (I've >>> got a bunch I wrote for my own mocking framework - everyone >>> should write >>> a mocking framework at least twice once :) >>> >>> The method matches?(actual) seems fairly universal across these >>> frameworks. >>> >> >> >> Something like this has been on the drawing board for a while. >> >> You can currently achieve something similar by using a block on >> the call to >> with(), but I admit the syntax is not great. >> >> expects(:chew).with { |mouthful1, mouthful2| mouthful1.is_a? >> (Chewable) } >> >> 2. Type-aware mocks that fail with a NoMethodError if the underlying >> >>> type doesn't respond to an invoked method or method_missing (see >>> earlier >>> thread). >>> >> >> >> >> 3. Something akin to after(another_mock, :method). >> >>> None of these are showstoppers, but 1 is really high on my >>> priorities >>> and 2 and 3 would make my life happier. I might try to roll >>> something >>> for 1 if I get the time (read: unlikely!). >>> >>> >> >> Cool. My bandwidth is a bit limited at the moment too, but I'll >> see what we >> can do. >> >> Thanks for your constructive suggestions. >> >> > > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer From jamesthepiper at gmail.com Sun Mar 18 13:03:41 2007 From: jamesthepiper at gmail.com (James Moore) Date: Sun, 18 Mar 2007 10:03:41 -0700 Subject: [mocha-developer] Automatically mocking ActiveRecord methods Message-ID: <7cfed280703181003q2ac0a225v676f0aae214f3ad0@mail.gmail.com> Seems like I do a fair amount of mocking ActiveRecord's automatically generated routines when I'm testing controllers. Things like: @portalspace = PortalSpace.new :public_readable => true, :public_writable => true, :name => 'public' @portalspace.stubs(:id).returns(203) PortalSpace.stubs(:find).with(203).returns(@portalspace) PortalSpace.stubs(:find).with('203').returns(@portalspace) PortalSpace.stubs(:find).with(:first, {:conditions => ['portal_spaces.name = ?', 'public']}).returns(@portalspace) Is there value in having an addon that says something like: foo.stub_active_record that would add some set of mocks/stubs for things like foo.stub(:find).with(foo.id).return(foo) foo.stub(:find).with(foo.id.to_s).return(foo) foo.stub(:find_by_name).with(foo.name).return(foo) etc. - James Moore From blake at near-time.com Fri Mar 23 14:17:10 2007 From: blake at near-time.com (Blake Watters) Date: Fri, 23 Mar 2007 14:17:10 -0400 Subject: [mocha-developer] Mocha 0.4.0 Hates My App Message-ID: Hi all - I've recently made the leap from FlexMock to Mocha for mocks within my app. Today I decided to upgrade from Mocha 0.3.2 to Mocha 0.4.0. So I updated the gem, started rake and went to get some coffee. I came back 5 mins later and my units (which were clean) are now reporting 97 failures (!!!). Every failure reports a callstack like this: 18) Error: test_created_at(EntityTest): NoMethodError: You have a nil object when you didn't expect it! The error occured while evaluating nil.created_at ./test/unit/entity_test.rb:199:in `test_created_at' /opt/local/lib/ruby/gems/1.8/gems/mocha-0.4.0/lib/mocha/ test_case_adapter.rb:19:in `run' Interestingly, this method runs fine when run alone, but not in the test:units suite or when the entire EntityTest is run. It relies on a @entity from the setup method, which seems to be executing. My app is fairly large, with a number of Gem and plugin dependencies. Has anyone else had a similar problem or have any ideas? So far I haven't made any headway on finding the source. Cheers, Blake !DSPAM:389,4604195f6891618813547! From jamesmead44 at gmail.com Sun Mar 25 11:09:44 2007 From: jamesmead44 at gmail.com (James Mead) Date: Sun, 25 Mar 2007 16:09:44 +0100 Subject: [mocha-developer] Mocha 0.4.0 Hates My App In-Reply-To: References: Message-ID: <1db558f00703250809p30e41671lf6e8bd1afdbedabb@mail.gmail.com> Sorry to hear that. Thanks for the stack-trace, but there's not really enough information in your email - can you give us the code in the failing test and the code under test? The fact it only fails when run on its own suggests it may be relying on another test to run first. -- James. http://blog.floehopper.org From blake at near-time.com Sun Mar 25 13:28:29 2007 From: blake at near-time.com (Blake Watters) Date: Sun, 25 Mar 2007 13:28:29 -0400 Subject: [mocha-developer] Mocha 0.4.0 Hates My App In-Reply-To: <1db558f00703250809p30e41671lf6e8bd1afdbedabb@mail.gmail.com> References: <1db558f00703250809p30e41671lf6e8bd1afdbedabb@mail.gmail.com> Message-ID: <38AD7F37-F631-460A-B141-2416D2443016@near-time.com> It actually passes when run on its own or when the individual test file is run, but fails during the complete suite. I'll follow up with code shortly... On Mar 25, 2007, at 11:09 AM, James Mead wrote: > Sorry to hear that. Thanks for the stack-trace, but there's not really > enough information in your email - can you give us the code in the > failing > test and the code under test? The fact it only fails when run on > its own > suggests it may be relying on another test to run first. > > -- > James. > http://blog.floehopper.org > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer > > > > !DSPAM:389,4606b0e7278811420326529! From jamesmead44 at gmail.com Tue Mar 27 10:01:04 2007 From: jamesmead44 at gmail.com (James Mead) Date: Tue, 27 Mar 2007 15:01:04 +0100 Subject: [mocha-developer] Automatically mocking ActiveRecord methods In-Reply-To: <7cfed280703181003q2ac0a225v676f0aae214f3ad0@mail.gmail.com> References: <7cfed280703181003q2ac0a225v676f0aae214f3ad0@mail.gmail.com> Message-ID: <1db558f00703270701l3f35d334ved9e3f62fb13fce0@mail.gmail.com> I can see why you might want to do this. I'm not sure I would want to add another public instance method to Object - recently I've been looking into how to reduce the number of such methods added to Object. Also I don't really want to incorporate any more Rails-specific code into Mocha. The Mocha Rails plugin is currently little more than a conditional require statement. Can I suggest you consider creating your own Rails plugin and see how things go...? -- James. http://blog.floehopper.org From jamesmead44 at gmail.com Tue Mar 27 10:22:25 2007 From: jamesmead44 at gmail.com (James Mead) Date: Tue, 27 Mar 2007 15:22:25 +0100 Subject: [mocha-developer] using mocha with rspec In-Reply-To: <02747787-F41E-4728-B8B8-D7AC04E4DB5E@jayfields.com> References: <45F5C371.9050702@tastapod.com> <1db558f00703130226h7a2b7c4cvc469be9cdb20e40c@mail.gmail.com> <45F68A3E.7040505@tastapod.com> <1db558f00703130503i19c9ab04l569e62248d7d4eb2@mail.gmail.com> <45F69BEB.1040006@tastapod.com> <02747787-F41E-4728-B8B8-D7AC04E4DB5E@jayfields.com> Message-ID: <1db558f00703270722m1df1fb2aj7a8726753b983ea3@mail.gmail.com> Jay, David, Thanks for all your messages and patches. I've just got back from holiday so haven't had much time to digest them. I am keen to improve the parameter matching in Mocha and the JMock/Hamcrest-style approach is my favourite. I'll try and get something committed asap. -- James. http://blog.floehopper.org From hayafirst at gmail.com Fri Mar 30 12:40:24 2007 From: hayafirst at gmail.com (Yi Wen) Date: Fri, 30 Mar 2007 11:40:24 -0500 Subject: [mocha-developer] problem with using any_instance Message-ID: Hey all, I have a question with using mocha in my tests. In the same test file, I have two tests, def test_a klass.any_instance.stubs(:method_name).returns("something") klass.new.method_name ... end def test_b ... klass.new.method_name ... end where klass is some class when the tests run, test _a passes, but test_b had an error like this: test_b NoMethodError: undefined method `method_name' for # I put a puts 'unstub called' in unstub of any_instance_method.rb and it got called wen running test_a Any idea what the problem it could be? Thanks, Yi From chen.mingquan at gmail.com Fri Mar 30 12:57:47 2007 From: chen.mingquan at gmail.com (Matt Chen) Date: Sat, 31 Mar 2007 00:57:47 +0800 Subject: [mocha-developer] problem with using any_instance In-Reply-To: References: Message-ID: Every test case would be run separately in unit test system, which means test_a does not have any relationship with test_b and of coz this statement in test_a klass.any_instance.stubs(:method_name).returns("something") will not influence any code in method test_b. I think if you move that statement above to the method setup it will works for you, since this method would be invoked before each test case starting. On 3/31/07, Yi Wen wrote: > > Hey all, > > I have a question with using mocha in my tests. > > In the same test file, I have two tests, > > > def test_a > klass.any_instance.stubs(:method_name).returns("something") > klass.new.method_name > ... > end > > def test_b > ... > klass.new.method_name > ... > end > > > where klass is some class > > when the tests run, test _a passes, but test_b had an error like this: > > test_b > NoMethodError: undefined method `method_name' for # > > I put a > > puts 'unstub called' > > in unstub of any_instance_method.rb and it got called wen running test_a > > Any idea what the problem it could be? > > Thanks, > > Yi > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer > From hayafirst at gmail.com Fri Mar 30 13:33:15 2007 From: hayafirst at gmail.com (Yi Wen) Date: Fri, 30 Mar 2007 12:33:15 -0500 Subject: [mocha-developer] problem with using any_instance In-Reply-To: References: Message-ID: sorry I mis-presented my case. 1. method_name is an instance method of klass 2. I do NOT want to mock this method in test_b So I guess there is something missing in unstub chain upon exiting test_a or I need to do some teardown myself? Thanks Yi On 3/30/07, Matt Chen > Every test case would be run separately in unit test system, which means > test_a does not have any relationship with test_b and of coz this > statement > in test_a > > klass.any_instance.stubs(:method_name).returns("something") > > will not influence any code in method test_b. > > I think if you move that statement above to the method setup it will works > for you, since this method would be invoked before each test case > starting. > > > On 3/31/07, Yi Wen wrote: > > > > Hey all, > > > > I have a question with using mocha in my tests. > > > > In the same test file, I have two tests, > > > > > > def test_a > > klass.any_instance.stubs(:method_name).returns("something") > > klass.new.method_name > > ... > > end > > > > def test_b > > ... > > klass.new.method_name > > ... > > end > > > > > > where klass is some class > > > > when the tests run, test _a passes, but test_b had an error like this: > > > > test_b > > NoMethodError: undefined method `method_name' for # > > > > I put a > > > > puts 'unstub called' > > > > in unstub of any_instance_method.rb and it got called wen running test_a > > > > Any idea what the problem it could be? > > > > Thanks, > > > > Yi > > _______________________________________________ > > mocha-developer mailing list > > mocha-developer at rubyforge.org > > http://rubyforge.org/mailman/listinfo/mocha-developer > > > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer > From jamesmead44 at gmail.com Fri Mar 30 13:50:58 2007 From: jamesmead44 at gmail.com (James Mead) Date: Fri, 30 Mar 2007 18:50:58 +0100 Subject: [mocha-developer] problem with using any_instance In-Reply-To: References: Message-ID: <1db558f00703301050w48478a08lffb2f95795d8826b@mail.gmail.com> I've just tried to recreate your problem... require 'test/unit' require 'rubygems' require 'mocha' class AnyInstanceTest < Test::Unit::TestCase class Klass def method_name "original" end end def test_a Klass.any_instance.stubs(:method_name).returns("something") assert_equal "something", Klass.new.method_name end def test_b assert_equal "original", Klass.new.method_name end end ...but both tests pass. So I don't understand why you are having a problem. Please send us all the code for the simplest self-contained test case which illustrates your problem? What version of Mocha are you using? And are you using it as a gem or as Rails plugin? -- James. http://blog.floehopper.org From hayafirst at gmail.com Fri Mar 30 14:24:09 2007 From: hayafirst at gmail.com (Yi Wen) Date: Fri, 30 Mar 2007 13:24:09 -0500 Subject: [mocha-developer] problem with using any_instance In-Reply-To: <1db558f00703301050w48478a08lffb2f95795d8826b@mail.gmail.com> References: <1db558f00703301050w48478a08lffb2f95795d8826b@mail.gmail.com> Message-ID: I use it as a rails plugin and I am running on 0.30 I put this a_test.rb under RAILS_PROJ_ROOT/test/unit If running test_a and test_b separately, they work fine, but if running the whole test suite, it gave me this error: output "called" is a string I puts in unstub of AnyInstanceMethod Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.7.2 /lib/rake/rake_test_loader Started "called" .E Finished in 0.011905 seconds. 1) Error: test_b(ATest): NoMethodError: undefined method `method_name' for # /home/ywen/projects/my_proj/test/unit/a_test.rb:6:in `send' /home/ywen/projects/my_proj/test/unit/a_test.rb:6:in `initialize' /home/ywen/projects/my_proj/test/unit/a_test.rb:21:in `new' /home/ywen/projects/my_proj/test/unit/a_test.rb:21:in `test_b' /home/ywen/projects/my_proj/config/../vendor/plugins/mocha/lib/mocha/test_case_adapter.rb:19:in `__send_ _' /home/ywen/projects/my_proj/config/../vendor/plugins/mocha/lib/mocha/test_case_adapter.rb:19:in `run' Thanks! Yi -------------------------------------------------------------------------------- a_test.rb require File.dirname(__FILE__) + '/../test_helper' class A attr_reader :b def initialize @b = send('method_name') end private def method_name "Original" end end class ATest < Test::Unit::TestCase def test_a A.any_instance.stubs(:method_name).returns("stubs") assert_equal "stubs", A.new.b end def test_b assert_equal "Original", A.new.b end end On 3/30/07, James Mead wrote: > > I've just tried to recreate your problem... > > require 'test/unit' > require 'rubygems' > require 'mocha' > > class AnyInstanceTest < Test::Unit::TestCase > > class Klass > def method_name > "original" > end > end > > def test_a > Klass.any_instance.stubs(:method_name).returns("something") > assert_equal "something", Klass.new.method_name > end > > def test_b > assert_equal "original", Klass.new.method_name > end > > end > > ...but both tests pass. So I don't understand why you are having a > problem. > > Please send us all the code for the simplest self-contained test case > which > illustrates your problem? > > What version of Mocha are you using? And are you using it as a gem or as > Rails plugin? > > -- > James. > http://blog.floehopper.org > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer >