Forum: RSpec Mocking expectations on I/O operations

6461a16238ac32aeb894dcf256b04161?d=identicon&s=25 Rob Aldred (Guest)
on 2011-09-14 12:54
(Received via mailing list)
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
1df9fc8ddf084661265bbae74a8d0b43?d=identicon&s=25 Justin Ko (Guest)
on 2011-09-15 03:19
(Received via mailing list)
On Wed, Sep 14, 2011 at 6:51 AM, Rob Aldred <rob@stardotstar.com> wrote:

>
>  ['-full','_screen','_shadow','_back_shadow'].each do |f|
> after(:each) do
> out_dir.join('MenuIcon.png')
> I'm pretty sure I'm going about this the wrong way and making it difficult
> http://rubyforge.org/mailman/listinfo/rspec-users
Give `File.unstub(:delete)` in your `after` block.
6461a16238ac32aeb894dcf256b04161?d=identicon&s=25 Rob Aldred (Guest)
on 2011-09-15 11:28
(Received via mailing list)
Thanks Justin,
That isnt working... its erroring with:

The method `delete` was not stubbed or was already unstubbed

--
Rob Aldred

Software Developer
rob@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@stardotstar.com
E540cdc2ce3a8fc669d040143ed3d461?d=identicon&s=25 Ash Moran (Guest)
on 2011-09-15 12:19
(Received via mailing list)
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
1df9fc8ddf084661265bbae74a8d0b43?d=identicon&s=25 Justin Ko (Guest)
on 2011-09-15 13:43
(Received via mailing list)
On Thu, Sep 15, 2011 at 5:05 AM, Rob Aldred <rob@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`
6461a16238ac32aeb894dcf256b04161?d=identicon&s=25 Rob Aldred (Guest)
on 2011-09-15 13:43
(Received via mailing list)
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

--
85d99e7678d8720f6e00ab0f60fe6ea9?d=identicon&s=25 Andrew Premdas (Guest)
on 2011-09-16 00:49
(Received via mailing list)
On 14 September 2011 11:51, Rob Aldred <rob@stardotstar.com> wrote:
> The method in the lib that does this is:
> File.delete "#{tmp_path}#{f}.png" if File.exists? "#{tmp_path}#{f}.png"
> end
> 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.
> rspec-users@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
2cddfa1d5e719e79aeab4e351c1793e9?d=identicon&s=25 Alex Chaffee (alexch)
on 2011-09-16 03:00
(Received via mailing list)
E540cdc2ce3a8fc669d040143ed3d461?d=identicon&s=25 Ash Moran (Guest)
on 2011-09-16 15:51
(Received via mailing list)
On 16 Sep 2011, at 01:17, Alex Chaffee wrote:

> https://github.com/defunkt/fakefs might help too


I 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
This topic is locked and can not be replied to.