I''m speccing a small lib which manipulates image files using mini_magick. The lib creates various temporary files during the process, the lib then cleans up the temporary files at the end. I''m trying to mock expectations that the calls are made to File#delete Eg... File.should_receive(:delete).once.with("#{generator.tmp_path}-full\.png") File.should_receive(:delete).once.with("#{generator.tmp_path}-screen\.png") File.should_receive(:delete).once.with("#{generator.tmp_path}-shadow\.png") The method in the lib that does this is: Class IconGenerator ... private def cleanup # remove temporary files created during # image manipulation [''-full'',''_screen'',''_shadow'',''_back_shadow''].each do |f| File.delete "#{tmp_path}#{f}.png" if File.exists? "#{tmp_path}#{f}.png" end end ... end The expectations work fine, however, I also have an after block which also calls File.delete to clean up the final version of the manipulated test image after(:each) do # cleanup manipulated image File.delete out_dir.join(''MenuIcon.png'') if File.exists? out_dir.join(''MenuIcon.png'') end The File.delete call in the after block fails because its calling the mocked version of File.delete This results in the following error: Failure/Error: File.delete out_dir.join(''MenuIcon.png'') if File.exists? out_dir.join(''MenuIcon.png'') NoMethodError: undefined method `=~'' for #<Pathname:/Users/robaldred/Sites/egg/tmp/MenuIcon.png> My next step was to stub the write method for every instance of MiniMagick::Image However I will then no longer be able to test the cleanup because the delete method will never be called if the files don''t exist. I''m pretty sure I''m going about this the wrong way and making it difficult for myself here. Appreciate any help & feedback Thanks -- Rob
On Wed, Sep 14, 2011 at 6:51 AM, Rob Aldred <rob at stardotstar.com> wrote:> I''m speccing a small lib which manipulates image files using mini_magick. > The lib creates various temporary files during the process, the lib then > cleans up the temporary files at the end. > > I''m trying to mock expectations that the calls are made to File#delete > > Eg... > File.should_receive(:delete).once.with("#{generator.tmp_path}-full\.png") > File.should_receive(:delete).once.with("#{generator.tmp_path}-screen\.png") > File.should_receive(:delete).once.with("#{generator.tmp_path}-shadow\.png") > > The method in the lib that does this is: > > Class IconGenerator > ... > > private > > def cleanup > # remove temporary files created during > # image manipulation > [''-full'',''_screen'',''_shadow'',''_back_shadow''].each do |f| > File.delete "#{tmp_path}#{f}.png" if File.exists? "#{tmp_path}#{f}.png" > end > end > ... > end > > The expectations work fine, however, I also have an after block which also > calls File.delete to clean up the final version of the manipulated test > image > > after(:each) do > # cleanup manipulated image > File.delete out_dir.join(''MenuIcon.png'') if File.exists? > out_dir.join(''MenuIcon.png'') > end > > The File.delete call in the after block fails because its calling the > mocked version of File.delete > This results in the following error: > > Failure/Error: File.delete out_dir.join(''MenuIcon.png'') if File.exists? > out_dir.join(''MenuIcon.png'') > NoMethodError: > undefined method `=~'' for > #<Pathname:/Users/robaldred/Sites/egg/tmp/MenuIcon.png> > > > My next step was to stub the write method for every instance of > MiniMagick::Image > However I will then no longer be able to test the cleanup because the > delete method will never be called if the files don''t exist. > > I''m pretty sure I''m going about this the wrong way and making it difficult > for myself here. > Appreciate any help & feedback > > Thanks > -- > Rob > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-usersGive `File.unstub(:delete)` in your `after` block. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110914/32f93594/attachment.html>
Thanks Justin, That isnt working... its erroring with: The method `delete` was not stubbed or was already unstubbed -- Rob Aldred Software Developer rob at stardotstar.com twitter: stardotstar 47 Newton Street, Manchester, M1 1FT T: +44 (0) 161 236 9740 ___________________________________________________ This email and any files or ideas transmitted within it are sent in confidence and are intended solely for the use of the individual or entity to whom they are addressed. If you have received this email in error please notify the system manager at info at stardotstar.com On Thursday, 15 September 2011 at 01:52, Justin Ko wrote:> > > On Wed, Sep 14, 2011 at 6:51 AM, Rob Aldred <rob at stardotstar.com (mailto:rob at stardotstar.com)> wrote: > > I''m speccing a small lib which manipulates image files using mini_magick. > > The lib creates various temporary files during the process, the lib then cleans up the temporary files at the end. > > > > I''m trying to mock expectations that the calls are made to File#delete > > > > Eg... > > File.should_receive(:delete).once.with("#{generator.tmp_path}-full\.png") > > File.should_receive(:delete).once.with("#{generator.tmp_path}-screen\.png") > > File.should_receive(:delete).once.with("#{generator.tmp_path}-shadow\.png") > > > > The method in the lib that does this is: > > > > Class IconGenerator > > ... > > > > private > > > > def cleanup > > # remove temporary files created during > > # image manipulation > > [''-full'',''_screen'',''_shadow'',''_back_shadow''].each do |f| > > File.delete "#{tmp_path}#{f}.png" if File.exists? "#{tmp_path}#{f}.png" > > end > > end > > ... > > end > > > > The expectations work fine, however, I also have an after block which also calls File.delete to clean up the final version of the manipulated test image > > > > after(:each) do > > # cleanup manipulated image > > File.delete out_dir.join(''MenuIcon.png'') if File.exists? out_dir.join(''MenuIcon.png'') > > end > > > > The File.delete call in the after block fails because its calling the mocked version of File.delete > > This results in the following error: > > > > Failure/Error: File.delete out_dir.join(''MenuIcon.png'') if File.exists? out_dir.join(''MenuIcon.png'') > > NoMethodError: > > undefined method `=~'' for #<Pathname:/Users/robaldred/Sites/egg/tmp/MenuIcon.png> > > > > > > My next step was to stub the write method for every instance of MiniMagick::Image > > However I will then no longer be able to test the cleanup because the delete method will never be called if the files don''t exist. > > > > I''m pretty sure I''m going about this the wrong way and making it difficult for myself here. > > Appreciate any help & feedback > > > > Thanks > > -- > > Rob > > > > _______________________________________________ > > rspec-users mailing list > > rspec-users at rubyforge.org (mailto:rspec-users at rubyforge.org) > > http://rubyforge.org/mailman/listinfo/rspec-users > > Give `File.unstub(:delete)` in your `after` block. > > Attachments: > - ATT00001..txt >
On 15 Sep 2011, at 10:05, Rob Aldred wrote:> Thanks Justin, > That isnt working... its erroring with: > > The method `delete` was not stubbed or was already unstubbedHi Rob For reasons I could go into, when I''m coding myself I don''t usually stub out file system access or other third party systems. Rather, I write an adapter that behaves in the way I expect and test it using the real file system, then test the rest of the system against a mock. That avoids this category of problem. What you''re doing here appears to be mixing both real (from mini_magick) and stubbed (your own library''s) calls to filesystem code, and I think it''s the asymmetry that''s biting you. I could explain the above in more detail*, but in lieu of that, here''s another angle: can you run the code in a subdirectory per image and nuke that after? That way you only have one point to hit for the cleanup. HTH Ash * As a quick example, you might have a TempFileCleaner object in place of IconGenerator#cleanup, seeded with all the potential temp filenames, or some algorithm to figure them out -- http://www.patchspace.co.uk/ http://www.linkedin.com/in/ashmoran
Hey Ash, I like the idea of a wrapper. In fact I use this method for methods that need to shell out Maybe your right, the subdirectory would probably a better safer way of organising the temporary files. I''ll have a re-think. Thanks for the info. Rob -- On Thursday, 15 September 2011 at 11:08, Ash Moran wrote:> > On 15 Sep 2011, at 10:05, Rob Aldred wrote: > > > Thanks Justin, > > That isnt working... its erroring with: > > > > The method `delete` was not stubbed or was already unstubbed > > Hi Rob > > For reasons I could go into, when I''m coding myself I don''t usually stub out file system access or other third party systems. Rather, I write an adapter that behaves in the way I expect and test it using the real file system, then test the rest of the system against a mock. That avoids this category of problem. > > What you''re doing here appears to be mixing both real (from mini_magick) and stubbed (your own library''s) calls to filesystem code, and I think it''s the asymmetry that''s biting you. > > I could explain the above in more detail*, but in lieu of that, here''s another angle: can you run the code in a subdirectory per image and nuke that after? That way you only have one point to hit for the cleanup. > > HTH > > Ash > > * As a quick example, you might have a TempFileCleaner object in place of IconGenerator#cleanup, seeded with all the potential temp filenames, or some algorithm to figure them out > > -- > http://www.patchspace.co.uk/ > http://www.linkedin.com/in/ashmoran > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org (mailto:rspec-users at rubyforge.org) > http://rubyforge.org/mailman/listinfo/rspec-users
On Thu, Sep 15, 2011 at 5:05 AM, Rob Aldred <rob at stardotstar.com> wrote:> Thanks Justin, > That isnt working... its erroring with: > > The method `delete` was not stubbed or was already unstubbed >Woops, you''re using expectations, not stubs. Try `File.rspec_reset`> > -- > Rob Aldred > > Software Developer > rob at stardotstar.com > twitter: stardotstar > > 47 Newton Street, Manchester, M1 1FT > T: +44 (0) 161 236 9740 > ___________________________________________________ > > This email and any files or ideas transmitted within it are sent in > confidence and are intended solely for the use of the individual or > entity to whom they are addressed. If you have received this email > in error please notify the system manager at info at stardotstar.com > > On Thursday, 15 September 2011 at 01:52, Justin Ko wrote: > > > > > > > On Wed, Sep 14, 2011 at 6:51 AM, Rob Aldred <rob at stardotstar.com(mailto: > rob at stardotstar.com)> wrote: > > > I''m speccing a small lib which manipulates image files using > mini_magick. > > > The lib creates various temporary files during the process, the lib > then cleans up the temporary files at the end. > > > > > > I''m trying to mock expectations that the calls are made to File#delete > > > > > > Eg... > > > > File.should_receive(:delete).once.with("#{generator.tmp_path}-full\.png") > > > > File.should_receive(:delete).once.with("#{generator.tmp_path}-screen\.png") > > > > File.should_receive(:delete).once.with("#{generator.tmp_path}-shadow\.png") > > > > > > The method in the lib that does this is: > > > > > > Class IconGenerator > > > ... > > > > > > private > > > > > > def cleanup > > > # remove temporary files created during > > > # image manipulation > > > [''-full'',''_screen'',''_shadow'',''_back_shadow''].each do |f| > > > File.delete "#{tmp_path}#{f}.png" if File.exists? "#{tmp_path}#{f}.png" > > > end > > > end > > > ... > > > end > > > > > > The expectations work fine, however, I also have an after block which > also calls File.delete to clean up the final version of the manipulated test > image > > > > > > after(:each) do > > > # cleanup manipulated image > > > File.delete out_dir.join(''MenuIcon.png'') if File.exists? > out_dir.join(''MenuIcon.png'') > > > end > > > > > > The File.delete call in the after block fails because its calling the > mocked version of File.delete > > > This results in the following error: > > > > > > Failure/Error: File.delete out_dir.join(''MenuIcon.png'') if > File.exists? out_dir.join(''MenuIcon.png'') > > > NoMethodError: > > > undefined method `=~'' for > #<Pathname:/Users/robaldred/Sites/egg/tmp/MenuIcon.png> > > > > > > > > > My next step was to stub the write method for every instance of > MiniMagick::Image > > > However I will then no longer be able to test the cleanup because the > delete method will never be called if the files don''t exist. > > > > > > I''m pretty sure I''m going about this the wrong way and making it > difficult for myself here. > > > Appreciate any help & feedback > > > > > > Thanks > > > -- > > > Rob > > > > > > _______________________________________________ > > > rspec-users mailing list > > > rspec-users at rubyforge.org (mailto:rspec-users at rubyforge.org) > > > http://rubyforge.org/mailman/listinfo/rspec-users > > > > Give `File.unstub(:delete)` in your `after` block. > > > > Attachments: > > - ATT00001..txt > > > > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110915/28e8a9ba/attachment-0001.html>
On 14 September 2011 11:51, Rob Aldred <rob at stardotstar.com> wrote:> I''m speccing a small lib which manipulates image files using mini_magick. > The lib creates various temporary files during the process, the lib then cleans up the temporary files at the end. > > I''m trying to mock expectations that the calls are made to File#delete > > Eg... > File.should_receive(:delete).once.with("#{generator.tmp_path}-full\.png") > File.should_receive(:delete).once.with("#{generator.tmp_path}-screen\.png") > File.should_receive(:delete).once.with("#{generator.tmp_path}-shadow\.png") > > The method in the lib that does this is: > > Class IconGenerator > ... > > ?private > > ?def cleanup > ?# remove temporary files created during > ?# image manipulation > ?[''-full'',''_screen'',''_shadow'',''_back_shadow''].each do |f| > ?File.delete "#{tmp_path}#{f}.png" if File.exists? "#{tmp_path}#{f}.png" > ?end > ?end > ... > end > > The expectations work fine, however, I also have an after block which also calls File.delete to clean up the final version of the manipulated test image > > after(:each) do > ?# cleanup manipulated image > ?File.delete out_dir.join(''MenuIcon.png'') if File.exists? out_dir.join(''MenuIcon.png'') > ?end > > The File.delete call in the after block fails because its calling the mocked version of File.delete > This results in the following error: > > Failure/Error: File.delete out_dir.join(''MenuIcon.png'') if File.exists? out_dir.join(''MenuIcon.png'') > NoMethodError: > undefined method `=~'' for #<Pathname:/Users/robaldred/Sites/egg/tmp/MenuIcon.png> > > > My next step was to stub the write method for every instance of MiniMagick::Image > However I will then no longer be able to test the cleanup because the delete method will never be called if the files don''t exist. > > I''m pretty sure I''m going about this the wrong way and making it difficult for myself here. > Appreciate any help & feedback > > Thanks > -- > Rob > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >Hi Rob, First of all perhaps you are testing the wrong thing in the wrong place. As cleanup is a private method, why are you specifying (in this spec how it works). As far as IconGenerator is concerned currently all you care about is that it calls cleanup. You''re not interested in how that cleanup is implemented. If you want to specify how the cleanup works then make cleanup public - either in this class, or perhaps better yet in another class. e.g. describe "Cleanup.clean" it "should delete files" end ... end describe IconGenerator ... it should cleanup Cleanup.should_receive(:clean) ... end Once you separate the cleanup spec from the IconGenerator spec then the clash should disappear! You''ll never have to mock File.delete HTH Andrew ------------------------ Andrew Premdas blog.andrew.premdas.org
https://github.com/defunkt/fakefs might help too -- Alex Chaffee - alex at stinky.com http://alexch.github.com http://twitter.com/alexch
On 16 Sep 2011, at 01:17, Alex Chaffee wrote:> https://github.com/defunkt/fakefs might help tooI thought that too, but then it occurred to me there''s a chance mini_magick isn''t using Ruby''s filesystem code (it might be writing to the FileSystem with native code for example), so I didn''t put this in to confuse the situation. But yes, if it could be done, FakeFS might work well here. -- http://www.patchspace.co.uk/ http://www.linkedin.com/in/ashmoran