Dir.pwd() returns nil in Rspec test? - ruby

I am working on a Ruby application which needs to have a specific directory structure to work properly. To make sure this is the case, I create some temporary directories for testing (rspec). I am attempting to save the current directory, and restore it after the test is done, but it looks like Dir.pwd() returns nil. Is it possible not to have a current directory? This isn't documented anywhere...
Code:
before :each do
# make a directory to work in
#olddir = Dir.pwd() #=> returns nil???
#dir = Dir.mktmpdir('jekyll')
end
after :each do
Dir.chdir(#olddir) #=> this fails
FileUtils.rm_rf(#dir)
end
it "should not blow up" do
1.should == 1
end
If I change it to this it works fine, but it seems like bad form to change to the home directory for no reason:
before :each do
#dir = Dir.mktmpdir('jekyll')
end
after :each do
Dir.chdir() #=> works, but feels wrong
FileUtils.rm_rf(#dir)
end
it "should not blow up" do
1.should == 1
end

I'm answering my own question, because I figured out what was wrong. I should have included the whole test (duh), which looked more like this:
context 'one' do
before :all do
# make a directory to work in
#dir = Dir.mktmpdir('foo')
end
after :all do
FileUtils.rm_rf(#dir)
end
it 'should not blow up' do
1.should == 1
end
end # end context 'one'
context 'two' do
before :each do
# make a directory to work in
#olddir = Dir.pwd()
#dir = Dir.mktmpdir('bar')
Dir.chdir(#dir)
end
after :each do
Dir.chdir(#olddir)
FileUtils.rm_rf(#dir)
end
it "should not blow up" do
1.should == 1
end
end # end context 'two'
The problem (now of course, it is obvious) was that I was removing the pwd, and getting an ENOENT because the current directory had been unlinked. This isn't documented in Ruby, because it's a filesystem error and not an error in the Ruby code.
The lesson, I suppose, is that rspec doesn't create a new running environment from scratch in each new test (as I assumed it did). Lesson(s) learned.

Related

Ruby: exception wrongly raised when testing the creation of a directory

I have a module named FileSystem in my app, which executes basic filesystem functionality. Here is the relevant code of it.
module TxtDB
module FileSystem
def self.create_database(db)
fpdb = db_full_path(db)
Dir.mkdir(fpdb) unless ((not valid_parameter?(db)) or (not valid_database?(fpdb)))
end
private
def self.valid_parameter?(db)
raise TxtDB::NIL_PARAMETER_ERROR unless (not db == nil)
raise TxtDB::NOT_A_STRING_ERROR unless (db.is_a? String)
raise TxtDB::EMPTY_PARAMETER_ERROR unless (not db.empty?)
true
end
def self.valid_database?(db)
raise TxtDB::DATABASE_ALREADY_EXISTS_ERROR unless (not Dir.exist?(db_full_path(db)))
true
end
def self.db_full_path(db)
"#{TxtDB::BASE_DIRECTORY}/#{db}"
end
end
end
And this is my Rspec test for this feature
it 'raises a StandardError (Database already exists) if it receives the name of an existing database' do
base_path = TxtDB::BASE_DIRECTORY
if (not Dir.exist?(base_path)) then
Dir.mkdir(base_path)
end
db_path = File.join(TxtDB::BASE_DIRECTORY,'testedb')
if (not Dir.exist?(db_path)) then
Dir.mkdir(db_path)
end
expect {
TxtDB::FileSystem::create_database('testedb')
}.to raise_error(StandardError, TxtDB::DATABASE_ALREADY_EXISTS_ERROR)
end
It happens that when I run my tests I got this error
expected StandardError with "Database already exists", got #<Errno::EEXIST: File exists # dir_s_mkdir - txtdb/testedb>
As I see things, this should not happen, since I'm testing for the existence before calling Dir.mkdir. But I'm obviously wrong, since the error occurs. The question is: Where am I wrong?
==========
According to the suggestion of Peter Alfvin (see answer below), I change my method to
def self.create_database(db)
fpdb = db_full_path(db)
if (valid_parameter?(db) and valid_database?(fpdb)) then
Dir.mkdir(fpdb)
end
end
Now there is no doubt the validations are done beforehand. But I still get the same error.
Dir.exists?(path) returns true if path is a directory, otherwise it returns false. In valid_database?, you are passing it the full path name, which I would guess is pointing to a non-directory file.
See http://ruby-doc.org/core-2.1.2/Dir.html#method-c-exists-3F

What is Camping::Server.start invoking in /bin/camping?

I'm studying how the Camping web framework works right now, and I don't understand what the Camping::Server.start at line 10 in /bin/camping is doing.
I expected this to call the start method in /lib/camping/server.rb at line 131, and so I put a simple puts 'hello' statement at the beginning of that method, expecting that statement to be invoked when I ran /bin/camping. However, I never saw my puts statement get called, so I can only assume that it's not that start method getting called.
I feel like I'm missing something obvious here. Here is the link to the camping github page and the relevant sections of code:
Github: https://github.com/camping/camping
From /bin/camping:
#!/usr/bin/env ruby
$:.unshift File.dirname(__FILE__) + "/../lib"
require 'camping'
require 'camping/server'
begin
Camping::Server.start
rescue OptionParser::ParseError => ex
puts "did it error"
STDERR.puts "!! #{ex.message}"
puts "** use `#{File.basename($0)} --help` for more details..."
exit 1
end
From /lib/server.rb:
def start
if options[:server] == "console"
puts "** Starting console"
#reloader.reload!
r = #reloader
eval("self", TOPLEVEL_BINDING).meta_def(:reload!) { r.reload!; nil }
ARGV.clear
IRB.start
exit
else
name = server.name[/\w+$/]
puts "** Starting #{name} on #{options[:Host]}:#{options[:Port]}"
super
end
end
My puts 'hello' on Camping::Server.start wasn't getting called because I didn't understand how static methods were defined in ruby.
start was being called statically, and I realize now that the start method I was looking at in the snippet wasn't a static method, which meant that another start method was getting called. I looked into Camping::Server and realized that it inherited from Rack::Server, which has the following method:
def self.start(options = nil)
new(options).start
end
That was the method getting called, not the one on /lib/camping/server.rb. I had been looking at the wrong method.

Accessing/Dealing with Variables in Ruby

Let me preface by stating I'm a "new" programmer - an IT guy trying his hand at his first "real" problem after working through various tutorials.
So - here is what I'm trying to do. I'm watching a directory for a .csv file - it will be in this format: 999999_888_filename.csv
I want to return each part of the "_" filename as a variable to pass on to another program/script for some other task. I have come up w/ the following code:
require 'rubygems'
require 'fssm'
class Watcher
def start
monitor = FSSM::Monitor.new(:directories => true)
monitor.path('/data/testing/uploads') do |path|
path.update do |base, relative, ftype|
output(relative)
end
path.create do |base, relative, ftype|
output(relative)
end
path.delete { |base, relative, ftype| puts "DELETED #{relative} (#{ftype})" }
end
monitor.run
end
def output(relative)
puts "#{relative} added"
values = relative.split('_',)
sitenum = values[0]
numrecs = values[1]
filename = values[2]
puts sitenum
end
end
My first "puts" gives me the full filename (it's just there to show me the script is working), and the second puts returns the 'sitenum'. I want to be able to access this "outside" of this output method. I have this file (named watcher.rb) in a libs/ folder and I have a second file in the project root called 'monitor.rb' which contains simply:
require './lib/watcher'
watcher = Watcher.new
watcher.start
And I can't figure out how to access my 'sitenum', 'numrecs' and 'filename' from this file. I'm not sure if it needs to be a variable, instance variable or what. I've played around w/ attr_accessible and other things, and nothing works. I decided to ask here since I've been spinning my wheels for a couple of things, and I'm starting to confuse myself by searching on my own.
Thanks in advance for any help or advice you may have.
At the top of the Watcher class, you're going to want to define three attr_accessor declarations, which give the behavior you want. (attr_reader if you're only reading, attr_writer if you're only writing, attr_accessor if both.)
class Watcher
attr_accessor :sitenum, :numrecs, :filename
...
# later on, use # for class variables
...
#sitenum = 5
...
end
Now you should have no problem with watcher.sitenum etc. Here's an example.
EDIT: Some typos.
In addition to Jordan Scales' answer, these variable should initialized
class Watcher
attr_accessor :sitenum, :numrecs, :filename
def initialize
#sitenum = 'default value'
#numrecs = 'default value'
#filename = 'default value'
end
...
end
Otherwise you'll get uninformative value nil

rspec shared_context and include_context for all specs

I'm trying to define a few let's and before hooks that will run globally for all my specs by including them in a separate file using the Rspec configuration block.
I tried something like:
module Helpers
def self.included(base)
base.let(:x){ "x" }
base.before(:all){ puts "x: #{x}" }
end
end
Rspec.configure{|c| c.include Helpers }
but this doesn't work as expected. The before(:all) doesn't just run before each main example group, but each nested one as well.
Then I found out about shared_context and it appears to be exactly what I want.
My open problem however is that I can't figure out how to share a context amongst ALL of my specs. The docs only reference include_context within a specific spec.
Can anyone tell me how I can achieve this behavior in a global manner? I'm aware that I can define global before hooks in my spec_helper but I can't seem to use let. I'd like a single place that I can define both of these things and not pollute my spec helper, but just include it instead.
I tried to reproduce your error, but failed.
# spec_helper.rb
require 'support/global_helpers'
RSpec.configure do |config|
config.include MyApp::GlobalHelpers
end
# support/global_helpers.rb
module MyApp
module GlobalHelpers
def self.included(base)
base.let(:beer) { :good }
base.before(:all) { #bottles = 10 }
end
end
end
# beer_spec.rb
require 'spec_helper'
describe "Brewery" do
it "makes good stuff" do
beer.should be :good
end
it "makes not too much bottles" do
#bottles.should == 10
end
context "when tasting beer" do
before(:all) do
#bottles -= 1
end
it "still produces good stuff" do
beer.should be :good
end
it "spends some beer on degusting" do
#bottles.should == 9
end
end
end
https://gist.github.com/2283634
When I wrote something like base.before(:all) { p 'global before'; #bottles = 10 }, I got exactly one line in spec output.
Notice that I didn't try to modify instance variables inside an example, because it wouldn't work anyway (well, actually you can modify instance variables, if it's a hash or array). Moreover, even if you change before(:all) in nested example group to before(:each), there will be still 9 bottles in each example.

Correct way to TDD methods that calls other methods

I need some help with some TDD concepts. Say I have the following code
def execute(command)
case command
when "c"
create_new_character
when "i"
display_inventory
end
end
def create_new_character
# do stuff to create new character
end
def display_inventory
# do stuff to display inventory
end
Now I'm not sure what to write my unit tests for. If I write unit tests for the execute method doesn't that pretty much cover my tests for create_new_character and display_inventory? Or am I testing the wrong stuff at that point? Should my test for the execute method only test that execution is passed off to the correct methods and stop there? Then should I write more unit tests that specifically test create_new_character and display_inventory?
I'm presuming since you mention TDD the code in question does not actually exist. If it does then you aren't doing true TDD but TAD (Test-After Development), which naturally leads to questions such as this. In TDD we start with the test. It appears that you are building some type of menu or command system, so I'll use that as an example.
describe GameMenu do
it "Allows you to navigate to character creation" do
# Assuming character creation would require capturing additional
# information it violates SRP (Single Responsibility Principle)
# and belongs in a separate class so we'll mock it out.
character_creation = mock("character creation")
character_creation.should_receive(:execute)
# Using constructor injection to tell the code about the mock
menu = GameMenu.new(character_creation)
menu.execute("c")
end
end
This test would lead to some code similar to the following (remember, just enough code to make the test pass, no more)
class GameMenu
def initialize(character_creation_command)
#character_creation_command = character_creation_command
end
def execute(command)
#character_creation_command.execute
end
end
Now we'll add the next test.
it "Allows you to display character inventory" do
inventory_command = mock("inventory")
inventory_command.should_receive(:execute)
menu = GameMenu.new(nil, inventory_command)
menu.execute("i")
end
Running this test will lead us to an implementation such as:
class GameMenu
def initialize(character_creation_command, inventory_command)
#inventory_command = inventory_command
end
def execute(command)
if command == "i"
#inventory_command.execute
else
#character_creation_command.execute
end
end
end
This implementation leads us to a question about our code. What should our code do when an invalid command is entered? Once we decide the answer to that question we could implement another test.
it "Raises an error when an invalid command is entered" do
menu = GameMenu.new(nil, nil)
lambda { menu.execute("invalid command") }.should raise_error(ArgumentError)
end
That drives out a quick change to the execute method
def execute(command)
unless ["c", "i"].include? command
raise ArgumentError("Invalid command '#{command}'")
end
if command == "i"
#inventory_command.execute
else
#character_creation_command.execute
end
end
Now that we have passing tests we can use the Extract Method refactoring to extract the validation of the command into an Intent Revealing Method.
def execute(command)
raise ArgumentError("Invalid command '#{command}'") if invalid? command
if command == "i"
#inventory_command.execute
else
#character_creation_command.execute
end
end
def invalid?(command)
!["c", "i"].include? command
end
Now we finally got to the point we can address your question. Since the invalid? method was driven out by refactoring existing code under test then there is no need to write a unit test for it, it's already covered and does not stand on it's own. Since the inventory and character commands are not tested by our existing test, they will need to be test driven independently.
Note that our code could be better still so, while the tests are passing, lets clean it up a bit more. The conditional statements are an indicator that we are violating the OCP (Open-Closed Principle) we can use the Replace Conditional With Polymorphism refactoring to remove the conditional logic.
# Refactored to comply to the OCP.
class GameMenu
def initialize(character_creation_command, inventory_command)
#commands = {
"c" => character_creation_command,
"i" => inventory_command
}
end
def execute(command)
raise ArgumentError("Invalid command '#{command}'") if invalid? command
#commands[command].execute
end
def invalid?(command)
!#commands.has_key? command
end
end
Now we've refactored the class such that an additional command simply requires us to add an additional entry to the commands hash rather than changing our conditional logic as well as the invalid? method.
All the tests should still pass and we have almost completed our work. Once we test drive the individual commands you can go back to the initialize method and add some defaults for the commands like so:
def initialize(character_creation_command = CharacterCreation.new,
inventory_command = Inventory.new)
#commands = {
"c" => character_creation_command,
"i" => inventory_command
}
end
The final test is:
describe GameMenu do
it "Allows you to navigate to character creation" do
character_creation = mock("character creation")
character_creation.should_receive(:execute)
menu = GameMenu.new(character_creation)
menu.execute("c")
end
it "Allows you to display character inventory" do
inventory_command = mock("inventory")
inventory_command.should_receive(:execute)
menu = GameMenu.new(nil, inventory_command)
menu.execute("i")
end
it "Raises an error when an invalid command is entered" do
menu = GameMenu.new(nil, nil)
lambda { menu.execute("invalid command") }.should raise_error(ArgumentError)
end
end
And the final GameMenu looks like:
class GameMenu
def initialize(character_creation_command = CharacterCreation.new,
inventory_command = Inventory.new)
#commands = {
"c" => character_creation_command,
"i" => inventory_command
}
end
def execute(command)
raise ArgumentError("Invalid command '#{command}'") if invalid? command
#commands[command].execute
end
def invalid?(command)
!#commands.has_key? command
end
end
Hope that helps!
Brandon
Consider refactoring so that the code that has responsibility for parsing commands (execute in your case) is independent of the code that implements the actions (i.e., create_new_character, display_inventory). That makes it easy to mock the actions out and test the command parsing independently. You want independent testing of the different pieces.
I would create normal tests for create_new_character and display_inventory, and finally to test execute, being just a wrapper function, set expectations to check that the apropriate command is called (and the result returned). Something like that:
def test_execute
commands = {
"c" => :create_new_character,
"i" => :display_inventory,
}
commands.each do |string, method|
instance.expects(method).with().returns(:mock_return)
assert_equal :mock_return, instance.execute(string)
end
end

Resources