I''ve found myself mocking out a published api in my set-up method. This has led to some contexts having a large set-up and empty specifications. As the mocks get auto-verified, the specification blocks have nothing to do but serve as documentation. Does this sound bad? Chris
aslak hellesoy
2006-Jul-24 17:40 UTC
[Rspec-users] Mocking causes empty specification blocks
On 7/24/06, Chris Roos <chrisjroos at gmail.com> wrote:> I''ve found myself mocking out a published api in my set-up method.What do you mean by mocking a published API. Are you setting up expectations for all of the API''s methods?> This has led to some contexts having a large set-up and empty > specifications. As the mocks get auto-verified, the specification > blocks have nothing to do but serve as documentation. Does this sound > bad? >I''m not sure you''re using mocks the way they are inteded to be used. The purpose of a mock is to verify that a certain conversation (i.e. message passing) takes place between two objects. You should only mock the methods that you expect to be invoked in a certain situation. Don''t set up expectations for all methods in your mocked API. Aslak> Chris > _______________________________________________ > Rspec-users mailing list > Rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
David Chelimsky
2006-Jul-24 18:58 UTC
[Rspec-users] Mocking causes empty specification blocks
On 7/24/06, Chris Roos <chrisjroos at gmail.com> wrote:> On 7/24/06, David Chelimsky <dchelimsky at gmail.com> wrote: > > On 7/24/06, Chris Roos <chrisjroos at gmail.com> wrote: > > > I''ve found myself mocking out a published api in my set-up method. > > > This has led to some contexts having a large set-up and empty > > > specifications. As the mocks get auto-verified, the specification > > > blocks have nothing to do but serve as documentation. Does this sound > > > bad? > > > > What is it that you are specifying? Why are there no statements in the specs? > > > I''m specifying the relationship between my object and the mocks. The > expectations on the mock object are created in the setup block and > auto-verified in my empty specification. > > I''ve pasted an example context below. I''m actually wanting to ensure > that a validator doesn''t get installed. All the expectations (bar > :header_row?) are irrelevant in this case; but I don''t think I can use > a null_object mock as I then wouldn''t get notified if we try to > install a validator. > > Chris > > -- spec -- > > context "A processed feed validator with one header row but no > required headers" do > > setup do > header_row = mock(''header_row'') > header_row.should_receive(:header_row?).and_return(true) > header_row.should_receive(:extend).with(Validatable) > header_row.should_receive(:errors) > header_row.should_receive(:fields).and_return([[]]) > io = [header_row] > feed_validator = FeedValidator.new(io) > feed_validator.process > end > > specify "should not install an empty header validator" do > end > > endIt strikes me that you''re mixing setup w/ events. Since your spec is that "should not install an empty header validator", then put something in the specify block that indicates when the installation is happening, and perhaps the specific expectation as well. For example: context "A processed feed validator with one header row but no required headers" do setup do @header_row = mock(''header_row'') @header_row.should_receive(:header_row?).and_return(true) @header_row.should_receive(:extend).with(Validatable) @header_row.should_receive(:errors) @io = [header_row] @feed_validator = FeedValidator.new(io) end specify "should not install an empty header validator" do @header_row.should_receive(:fields).and_return([[]]) @feed_validator.process end end Also, you can use null_object to tell the mock to not worry about calls you don''t specify. So, for example, you could do this in setup: @header_row = mock(''header_row'', :null_object => true) and then you don''t need this in your setup: @header_row.should_receive(:extend).with(Validatable) @header_row.should_receive(:errors) The only things you need in the setup would be the messages that result in some return value. Then you could have separate specify blocks for "should supply errors" and "should extend using a Validitable" (for example).
On 7/24/06, David Chelimsky <dchelimsky at gmail.com> wrote:> On 7/24/06, Chris Roos <chrisjroos at gmail.com> wrote: > > On 7/24/06, David Chelimsky <dchelimsky at gmail.com> wrote: > > > On 7/24/06, Chris Roos <chrisjroos at gmail.com> wrote: > > > > I''ve found myself mocking out a published api in my set-up method. > > > > This has led to some contexts having a large set-up and empty > > > > specifications. As the mocks get auto-verified, the specification > > > > blocks have nothing to do but serve as documentation. Does this sound > > > > bad? > > > > > > What is it that you are specifying? Why are there no statements in the specs? > > > > > I''m specifying the relationship between my object and the mocks. The > > expectations on the mock object are created in the setup block and > > auto-verified in my empty specification. > > > > I''ve pasted an example context below. I''m actually wanting to ensure > > that a validator doesn''t get installed. All the expectations (bar > > :header_row?) are irrelevant in this case; but I don''t think I can use > > a null_object mock as I then wouldn''t get notified if we try to > > install a validator. > > > > Chris > > > > -- spec -- > > > > context "A processed feed validator with one header row but no > > required headers" do > > > > setup do > > header_row = mock(''header_row'') > > header_row.should_receive(:header_row?).and_return(true) > > header_row.should_receive(:extend).with(Validatable) > > header_row.should_receive(:errors) > > header_row.should_receive(:fields).and_return([[]]) > > io = [header_row] > > feed_validator = FeedValidator.new(io) > > feed_validator.process > > end > > > > specify "should not install an empty header validator" do > > end > > > > end > > It strikes me that you''re mixing setup w/ events. Since your spec is > that "should not install an empty header validator", then put > something in the specify block that indicates when the installation is > happening, and perhaps the specific expectation as well. For example: > > context "A processed feed validator with one header row but no > required headers" do > > setup do > @header_row = mock(''header_row'') > @header_row.should_receive(:header_row?).and_return(true) > @header_row.should_receive(:extend).with(Validatable) > @header_row.should_receive(:errors) > @io = [header_row] > @feed_validator = FeedValidator.new(io) > end > > specify "should not install an empty header validator" do > @header_row.should_receive(:fields).and_return([[]]) > @feed_validator.process > end > > end >Maybe it would help if I pasted how I would like the code to look.. context "A feed validator with one header row but no required headers" do setup do @header_row = mock(''header_row'') @header_row.should_receive(:header_row?).and_return(true) io = [@header_row] @feed_validator = FeedValidator.new(io) end specify "should not install an empty header validator" do @header_row.should_not_receive(:install_validators) @feed_validator.process end end Anything that was in my original example that is not above was there because otherwise my mock will complain of unexpected methods being called. I don''t think I''m able to use the :null_object option as I''m relying on the fact that my mock complains if the install_validators message is sent. Note the made up ''should_not_reveive'' method above. Maybe if I had something like this then I could indeed use the null_object option? As for having the feed_validator.process line in the setup, that was kinda taken from the rSpec tutorial (re ''should not be empty after push''). I was thinking along the lines of this object already being processed. Thanks for the help guys. Chris> Also, you can use null_object to tell the mock to not worry about > calls you don''t specify. So, for example, you could do this in setup: > > @header_row = mock(''header_row'', :null_object => true) > > and then you don''t need this in your setup: > > @header_row.should_receive(:extend).with(Validatable) > @header_row.should_receive(:errors) > > The only things you need in the setup would be the messages that > result in some return value. Then you could have separate specify > blocks for "should supply errors" and "should extend using a > Validitable" (for example). > _______________________________________________ > Rspec-users mailing list > Rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
On 7/25/06, David Chelimsky <dchelimsky at gmail.com> wrote:> On 7/24/06, Chris Roos <chrisjroos at gmail.com> wrote: > > Maybe it would help if I pasted how I would like the code to look.. > > > > context "A feed validator with one header row but no required headers" do > > > > setup do > > @header_row = mock(''header_row'') > > @header_row.should_receive(:header_row?).and_return(true) > > io = [@header_row] > > @feed_validator = FeedValidator.new(io) > > end > > > > specify "should not install an empty header validator" do > > @header_row.should_not_receive(:install_validators) > > @feed_validator.process > > end > > > > end > > > > Anything that was in my original example that is not above was there > > because otherwise my mock will complain of unexpected methods being > > called. I don''t think I''m able to use the :null_object option as I''m > > relying on the fact that my mock complains if the install_validators > > message is sent. Note the made up ''should_not_reveive'' method above. > > Maybe if I had something like this then I could indeed use the > > null_object option? > > > > As for having the feed_validator.process line in the setup, that was > > kinda taken from the rSpec tutorial (re ''should not be empty after > > push''). I was thinking along the lines of this object already being > > processed. > > null_object only ignores things that you do not specify. If you > specify that something should not be received, it should complain if > it receives it. If it does not complain, raise a bug! That''s how it > should work. > > Cheers, > David >Ahh, I couldn''t previously find anyway of specifying that something should not be called (I was trying should_not). I''ve just realised that I need to do should_receive(:foo).never. Not sure if it''s possible but I''d guess the should_not_receive may be more intuitive? With this info, I''ll tidy my specs and see how they now look. Thanks for all the help. Chris
David Chelimsky
2006-Jul-25 14:49 UTC
[Rspec-users] Mocking causes empty specification blocks
On 7/25/06, Chris Roos <chrisjroos at gmail.com> wrote:> Ahh, I couldn''t previously find anyway of specifying that something > should not be called (I was trying should_not). I''ve just realised > that I need to do should_receive(:foo).never. > > Not sure if it''s possible but I''d guess the should_not_receive may be > more intuitive?I think you''re right. Can you raise this as an issue at rubyforge?
On 7/25/06, David Chelimsky <dchelimsky at gmail.com> wrote:> On 7/25/06, Chris Roos <chrisjroos at gmail.com> wrote: > > > Ahh, I couldn''t previously find anyway of specifying that something > > should not be called (I was trying should_not). I''ve just realised > > that I need to do should_receive(:foo).never. > > > > Not sure if it''s possible but I''d guess the should_not_receive may be > > more intuitive? > > I think you''re right. Can you raise this as an issue at rubyforge?Added as a feature request. Chris