What strategy should I use to unit test reading a file in ruby (rspec)? - ruby

I've got a ruby method which processes a very large file line-by-line (ie I cannot load the file into memory) like the following:
def process_file
file = File.new(##data_file, "r")
while (line = file.gets)
{ do something with the line } ...
end
file.close
return "upload complete"
end
As per best practices, I do not want this this method to have any reliance on the filesystem. I've used stubs before, but I'm not sure how I would go about using it here. I would imagine I would use something equivalent to:
file = File.stub!(:new).and_return({something})
I'm just not sure what {something} is.
I am using rspect and Any assistance would be appreciated. Thanks.

You could always check if you call proper methods, at least the close() after. I think that's the most often error with i/o.

You might want to look at MockFS, which mocks out the operation of the filesystem.

I don't think that a unit test is called for here. Are you afraid that, somehow, creating files/closing files is going to be broken in some future version of Ruby? It's not, don't worry.
Let the Ruby devs worry about unit testing their platform specific file IO routines. You should worry about unit testing your algorithms, and specifically those algorithms which are critical and complex (i.e., prone to breakage if you're not careful).
That said, you should probably just pass a block to the constructor. This ensures that the file will be closed for you when the block exits and is more idiomatic.
File.open(some_file_path, 'w+') do |file|
file.puts('content')
end

Related

Using Minitest to Test File Write

Is there a way to use minitest to mock/stub file reads and writes (text files) without actually having them existing?
the actual code is something like
def write_to_file(filename)
f = File.open(filename,"w")
f.puts "hello world"
f.close
end
I really don't know how to try something since I couldn't find any examples for mocking File IO, only comparing variables
If I were going to do what you're suggesting, I would stub File.open to return a mock like:
def test_something_that_reads_a_file
file_mock = Minitest::Mock.new
file_mock.expect(:readline, "It was the best of times...")
File.stub(:open, file_mock) do
# your test logic here
end
file_mock.verify
end
You still have the problem of returning EOF at some point, but whether that's an issue for you depends on context that I don't have given what you've written.
But there are a couple of reasons why I would probably never approach the problem like this.
Don't mock what you don't own. Yes, the semantics of File#readline are probably not going to change tomorrow, but making assumptions about an interface that's outside your control is a bad practice.
To me, this reads like a non-public method. If I'm right about that, then I wouldn't test it at all. Private methods are implementation details that don't need their own tests. If you're dead set on not doing any I/O here, I'd rather stub the result of the method rather than what I've done above.
The approach I would take would be to include a small test file and actually read from it. Your tests will be easier to understand as a result.

How to iterate for lazy compilation

How in Ruby do I ...
iterate over two folders (paths given as an initial constant or paths taken from a json file)
and compare their time stamps, triggering a certain command (an empty foo()-function for now, just printing the filename) on the former, if it's timestamp is same-or-old than the latter.
I want to build a lazy aka lean update mechanism, much like make in the old days, which only re-compiled C files, if they are newer.
[ And that, just to let you know, I want to do, to bypass some heavy corporate JEE deployment mechanism, where in fact only one or two css/jsp files keep changing (as frontend designers do) and need to be pushed from dev to server root. :-]
Forgive me the scope of the question, I am a complete noob to file operations in ruby... but it seems the best available cross-plattform scripting in my environment at hand.
def foo(bar)
puts bar
end
def bar(first,second)
puts "#{first} is not older than #{second}"
end
def compare_time_stamps
Dir.foreach("path/one") do |filename|
#first_file << File.ctime(filename)
end
Dir.foreach("path/two") do |filename|
#second_file << File.ctime(filename)
end
#first_file.zip(#second_file)
#first_file.each do |first_time,second_time|
#first_time.parse_time >= #second_time.parse_time ? foo(#first_time) : bar(#first_time,#second_time)
end
end
edit--
Which time stamps are you trying to compare #Fronker? Are you trying to compare file1 of folder 1 with file1 of folder 2 or file1 of folder 1 with file 2 of folder 1
This is not tested, but with some editing it should work. You will need to make a method -- perhaps using regex -- to parse the 'ctime' method into a time stamp format that you want. This is really rough and i'm not for sure if it will even work like it is supposed to so take it lightly with a grain of salt. When I get home I will edit it to be better but for now this should get you started with which methods might work.
Hopfully this helps somewhat, Happy coding!
Actually, there is a build system written in Ruby, called rake (stems from Ruby Make) that already has an implementation of detecting changed task dependencies and only running a chain of tasks that are affected.
You might find this introduction to rake useful.

Structure of Ruby Programs

I need some insight into the construction of Ruby programs. I'm trying to learn how to write Ruby (independent of Rails) so I'm translating some Perl scripts I wrote in a bioinformtatics project into Ruby code. Basically creating classes where useful and whatnot.
My issue is how do I execute it? The Perl scripts are just long blocks of commands, one after the other. What's appropriate in Ruby? Should I define my classes in their own .rb files and call those and their methods in a sepearate rb file that sort of uses them to execute my program?
What is normally done? Any examples would be greatly apreciated. I'd also appreciate any tips in general on how to go about learning this kind of thing.
Ruby does have what's usually called the top level execution environment, and so a long string of commands will execute immediately just like Perl. Or, you can define classes and modules and go all OOP on your problem if you want, or you can mix the approaches.
You will need at least one line at the top level or top level of a class to start everything off. So:
p :hello
or
class A
p :hello
end
or
class A
def run
p :hello
end
end
A.new.run
or, my favorite:
class A
def run
p :hello
end
self
end.new.run
I'd highly recommend looking at some of your other favorite gems to see how their code is structured (like on Github). That's how I found my start. Thinking of your project as a "gem", being released or not, is a good way to wrap your mind around the problem.

Using "should" with class methods?

I'm used to making calls such as:
new_count.should eql(10)
on variables, but how can I do something similar with a class method such as File.directory?(my_path)?
Every combination of File.should be_directory(my_path) that I've tried leads to a method missing, as Ruby tries to find "be_directory" on my current object, rather than matching it against File.
I know I can turn it around and write
File.directory?(my_path).should == true
but that gives a really poor message when it fails.
Any ideas?
Hmm, maybe I have an idea.
File is part of Ruby proper, so it may have elements written in C. Some of Ruby's meta-programming tools break down when dealing with classes imported from C, that could explain Rspec's failure to make .should behave as expected.
If that's true, there is no real solution here. I'd suggest using the MockFS library:
http://mockfs.rubyforge.org/
This downside to MockFS is using it everywhere you'd normally use File, Dir and FileUtils:
require 'mockfs'
def move_log
MockFS.file_utils.mv( '/var/log/httpd/access_log', '/home/francis/logs/' )
end
The upside, especially if your code is file-intensive, is the ability to spec really complex scenarios out, and have them run without actually touching the slow filesystem. Everything happens in memory. Faster, more complete specs.
Hope this helps, Good luck!
I'm not sure why be_directory wouldn't work for you. What version of rspec are you using? You can also use rspec's predicate_matchers method, when a predicate exists, but it doesn't read nicely as be_predicate.
Here's what I tried:
describe File, "looking for a directory" do
it "should be directory" do
File.should be_directory("foo")
end
predicate_matchers[:find_the_directory_named] = :directory?
it "should find directory" do
File.should find_the_directory_named("foo")
end
end
And that gave me the following output (run with spec -fs spec.rb):
File looking for a directory
- should be directory
- should find directory
Finished in 0.004895 seconds
2 examples, 0 failures

Passing a parameter/object to a ruby unit/test before running it using TestRunner

I'm building a tool that automates a process then runs some tests on it's own results then goes to do some other stuff.
In trying to clean up my code I have created a separate file that just has the test cases class. Now before I can run these tests, I have to pass the class a couple of parameters/objects before they can be run. Now the problem is that I can't seem to find a way to pass a parameter/object to the test class.
Right now I am thinking to generate a Yaml file and read it in the test class but it feels "wrong" to use a temporary file for this. If anyone has a nicer solution that would be great!
**************Edit************
Example Code of what I am doing right now:
#!/usr/bin/ruby
require 'test/unit/ui/console/testrunner'
require 'yaml'
require 'TS_SampleTestSuite'
automatingSomething()
importantInfo = getImportantInfo()
File.open('filename.yml', 'w') do |f|
f.puts importantInfo.to_yaml
end
Test::Unit::UI::Console::TestRunner.run(TS_SampleTestSuite)
Now in the example above TS_SampleTestSuite needs importantInfo, so the first "test case" is a method that just reads in the information from the Yaml file filname.yml.
I hope that clears up some confusion.
Overall, it looks like you're not really using the unit tests in a very rubyish way, but I'll leave that aside for a minute.
Your basic problem is that you have some setup that needs to happen before the tests run. The normal way to do that is with a setup method within the test unit case itself.
class UserTest < TestUnit::TestCase
def setup
# do your important calculation
end
def test_success
#.. assert some things
end
end
I would give some thought to what code it is that you're actually testing here, and see if you can break it down and test it in a more granular way, with lots more tests.
First, I agree with Cameron, this code definitely does not adhere to the Ruby way, though I'll also sidestep that for now.
The fastest way to get up and running with this, especially if this data is pretty much immutable (that is to say, your tests won't be altering it in anyway), is to just assign the value to a constant. So instead of naming your variable importantInfo, you name it IMPORTANT_INFO. Then it will be available to you in your tests. It's definitely not a pretty solution, and I think it couuld even be considered a test smell that you need that sort of global setup, but it's there for you.
Alternatively, you could look at stubbing out the importantInfo, which I actually think would provide for much cleaner and more readable tests.

Resources