How to test file creation with RSpec? - ruby

I have a simple FileCreator Ruby class that has 1 method create which creates a blank txt file on my desktop. Using RSpec, how would I test this create method to make sure
that the file was created, without having to create the file? Would I use RSpec::Mocks? Can someone please point me in the right directory? Thanks!

After calling file_creator.create(100) you could search the folder for all File*.txt files and make sure the count matches. (Make sure to have your spec remove the test files after completion).
Dir.glob(File.join(File.expand_path("~/Desktop"), "File*.txt")).length.should == 100
Using Mocks: You could do something like this to verify that the File.open method is actually being called (to test that the files actually get created, though, you may want to consider actually creating the files like the first half of my answer).
File.should_receive(:open).exactly(100).times

You could also try using something like FakeFS which mocks the actual file system.

The simplest way to do it is as below:
FileCreator.count.should eq 100

Related

Is there a way I can use a different extension than .txt such as .log or .csv when using ApprovalTests?

I want the content to remain a text file, so code such as:
Approvals.Verify("some actual result as text");
Should continue to work. But I would like the approval tests to write to a ..approval.csv file or a ..approval.log file instead of ..approval.txt file.
I looked through the code and I think I may need to create a custom IApprovalWriter, but I also notice that the ExtensionWithDot property is read/write, so I'm wondering how this can be set.
Does anyone know how to do that?
ApprovalTests.Approvals.Verify(WriterFactory.CreateTextWriter(text, fileExtensionWithoutDot));
or for your example:
Approvals.Verify(WriterFactory.CreateTextWriter("some actual result as text", "log"));
Happy Testing!

Ruby: Random selection of files/classes

I'm trying to write a little text rpg kind of game. What I'm trying to do is make it so that there are multiple little adventures, each contained in their own source file. I want to be able to select one source file at random, create an instance of its contained class, and then call a function (we'll call it Adventure#start). I'm having trouble thinking of a way to accomplish this without using a crazy array/hash and a big case tree, all of which would need to be updated with every adventure added...
I feel like there's something obvious I'm missing, but is there a practical way to go about this? The main point being that I would want to not have to update other code just to add a new adventure, but rather simply add the source for said adventure, drop the file into the appropriate folder, and be done with it.
You can do it with the following steps:
Get all the files from the folder containing adventure files
Choose a random file
Require the chosen file
Parse the content of the chosen file for the class name
Create an instance of the adventure using (Kernel.const_get class_name).new
Call #start on the newly created instance

Testing File/Folder Navigation and Manipulation

I am working on a module that supplies methods for navigating directories and manipulating files. Basically it will be a combination of the Dir and File classes, with options specific to the needs of a project I'm working on.
Right now I have started writing tests for some of these methods and things are getting messy.
Example
One of the methods I have is a tree function that returns a hash of files and folders where you can pass options like tree(only: 'folders', limit: 3). In order to test that it only goes down 3 levels, I would have to have 4+ subfolders with dummy files in them.
The Problem
Right now I'm testing on folders outside the project since the subfolders are already there, but I want to move away from this, especially considering the implausibility of testing on system files once I start testing methods equivalent to rm -rf (as well as the lack of portability).
I'm starting to think that I need to create a "lab rat" type folder that I do all my "experiments" on, but I have no clue how to approach creating it.
Do I create a function that creates the files?
Do I pull files and folders from another location?
Do I use some sort of "lorem ipsum" generator for file structures?
Do I make all these files and folders manually(ugh)?
Do I just mock and stub the hell out of everything and not actually create/delete the files and folders?(I don't see this happening)
So...
How would someone normally approach testing excessive amounts of file and folder manipulation?
I don't think you want to use mocks/stubs. The file system of your OS should be well tested and fast, so the benefit of mocks/stubs is minimal. Creating a mock/stub system increases the complexity without much benefit.
Here's my answers:
Do I create a function that creates the files?
Yes. You can create tests for these functions to make sure that they are correct. Instead of calling Dir and File, write helper functions that make the code simple and readable. Maybe you can share the helper functions between the source/test code...
Do I pull files and folders from another location?
Not sure what this is for...
Do I use some sort of "lorem ipsum" generator for file structures?
Yes, if you mean create functions that generate file structures.
Do I make all these files and folders manually(ugh)?
No.
Do I just mock and stub the hell out of everything and not actually create/delete the files and folders?(I don't see this happening)
No. One benefit of creating files/directories is that you can manually check what is going on and not be 100% dependent on the tests. This is actually a good approach because without it there could be a bug where both the source code and test code is not doing what you expect, but you wouldn't know because everything seems to be working.

Save WWW::Mechanize::File to disk using FileUtils

Using Mechanize with Ruby I get a certain file using agent.get('http://example.com/foo.torrent'), with FileUtils or otherwise, how do I save this file to my hard drive (for instance, in a directory wherefrom the script is running)?
P.S. class => WWW::Mechanize::File
Well, WWW::Mechanize::File has a save_as instance method, so I suppose something like this might work:
agent.get('http://example.com/foo.torrent').save_as 'a_file_name'
Please note that the Mechanize::File class is not the most appropriate for large files. In those cases, one should use the Mechanize::Download class instead, as it downloads the content in small chunks to disk. The file will be downloaded to where the script is running (although you can specify a different path as well). You need to set the default parser first, create a new one or modify an existing parser. Then, save it to the desired path:
agent.pluggable_parser.default = Mechanize::Download
agent.get( "http://example.com/foo.torrent}").save("path/to/a_file_name")
Check here and here for more details. Also, there's a similar question here in Stackoverflow.

Ruby - How to prevent wiping your hard drive when using delete file and directory commands in your code

I'm writing some code that at run time may create or delete directories within the project path. I haven't really used ruby for file processing so i'm really uneasy about having code that, with a few mistypes weeks down the line, could result in wiping other directories outside of my project path.
Is there anyway to make it impossible for the program to delete files outside of its own path regardless of whats typed in destructive calls?
Pathname is a wrapper class for almost any file operations.
require "pathname"
path= Pathname.new("/home/johannes")
path.directory? # => true
path.children # => [#<Pathname:.bash_history>, #<Pathname:Documents>, #<Pathname:Desktop>]
path.children.each do |p|
p.delete if p.file?
end
Pathname#children does not contain . or .. so you don't accidently walk up the tree instead of down. If you still don't trust in the code, you can even check if on path is contained in another
Pathname.new("test") <=> Pathname.new("test/123") # => -1
You might want to create a wrapper method around your favourite delete method (or, perhaps, around whole class, because not only deleting files is potentially destructive file operation), which would expand all the submitted paths and check whether they begin with your "sandbox" path). You can also try to redefine delete method, if you are willing to cripple it through whole application.
And maybe the cleanest solution of them all would be to create a new user on your system and run your program as him.
On a POSIX system, you can use Dir.chroot to change the root that your application sees. Then ALL actions, not just delete ones, will be limited to the project directory. This does mean that external commands will be unavailable unless you make them part of your project directory as well.
This is the standard 'sandboxing' method used in Unix based systems. It can be difficult to setup (eliminating all external dependancies is sometimes hard), but affords significant protection when configured properly.
You could generate an Array of filenames in your project directory using
my_files = Dir["/bla/bla/your/directory/**/*"]
and then simply check if the filename passed to your "delete" function exist in your my_files array.
I'm sure there is a more elegant solution, but this could work ^_^
You could use File.expand_path and File.dirname on the input, and check that against __FILE__. So something like this might work:
File.delete(path) if File.dirname(File.expand_path(path)).include? File.dirname(File.expand_path(__FILE__))
I've got automated tests that routinely create and wipe out directories. I've taken two approaches:
Use /tmp as much as possible. The 'tmpdir' standard library module will create temporary directories which will be destroyed when your program exits. Or,
When the code creates a directory that it will later be deleting, it drops a marker file into the directory. When it comes time to delete the directory, if the marker file is not found, the code refuses to delete the directory. A marker file might be called ".ok_to_delete", for example.

Resources