Well, there are a bunch of people making these kind of "guides", so I think it's time to make mine too...
In this guide, must and should are used to define mandatory or recomended, in that order.
- variable's and method's names must be in
snake_case
. - classes' and modules' names must be in
CamelCase
. - objects should follow classes' names, like
@person = Person.new
, orempty_person = Person.new
- any method that changes object's state should be named with an exclamation point:
method_name!
. It should be good too if these methods don't return a meaning result (ever had problems with"string".upcase!
returningnil
?) - any method that returns a boolean should be named like
method?
- any method that is named with an question mark (like
method?
) must return a boolean (true
orfalse
. Don't make Ruby's dynamic type more surprising than already is). - if the return of a
method?
is not a boolean but is an information related to the "question" of the method, it should be returned. For example:"I am Wally".is_there_wally? => 5
. But it must be meaningful.
- multiple identations should be avoided: separate your code on methods
def create_magazines
magazines = ThirdPartyBookstore.all
magazines.each { |m| create_magazine m }
end
def create_magazine(data)
magazine = Magazine.new data
magazine.save!
end
- "if" and "unless" as modifiers must be used everytime that is possible
def save
return unless valid?
end
- every
when
oncase
must never have more than one command:
message = case attribute
when String then "It's a String!"
when Numeric then "It's a Number!"
else "Don't know what this is..."
end
\#or
class ObjectFactory
def make(from_what)
case from_what
when :admin then AdminUpdater.new
when :user then instantiate_user_updater
end
end
end
- methods that are called from other methods must be below their callers
- these "dependent methods" must be in order which they are called
def a_method
calling_one
calling_two
calling_three
end
def calling_one
calling_three
end
def calling_two
puts "Woo"
end
def calling_three
puts "Three"
end
-
methods must have, at most, 30 lines (so they appear on only one screen)
-
to permit the above requirements,
private
should be used as a keyword, and must be used directly below the method:
def public_method
do_something
end
def do_something
end
private :do_something
I don't know who beginned with the idea of "describe Array#each" or such but...
- don't reference methods' names on description of specs. This ties internal structure to tests
- describe what that test is, in fact, testing:
it "creates a new user" do
context
anddescribe
are for grouping similar tests. Avoid using then too much, for example:
describe UsersController do
context "on POST" do
context "create" do
context "when giving valid data" do
it "..."
-
don't test internal structure. Ever. Yes, rspec-rails'
assigns(:instance_variable)
, I'm talking to you... -
make sure you're testing something. So, avoid things like:
it "not raises any error if user is valid" do
user = User.new :name => "foo"
user.save! #What are you testing here?
end
it "has many slaves" do
User.should have_many(:slaves) #What do you need this for?
end
it "is unique" do
User.stub!(:where).with(:name => "Foo").and_return([mock(User, :name => "Foo")])
user = Factory.stub :user, :name => "Foo"
user.should_not be_valid #Do you guarantee that this asserts uniqueness?
end
- don't use "implicit" values on your tests. What happens if one attribute cease to be mandatory, for example, or needs to change its default value on a factory?
it "concatenates name an surname" do
#WRONG WAY:
user = Factory :user
user.complete_name = "Foo Bar" #DON'T!
#CORRECT WAY:
user = Factory :user, :name => "Foo", :surname => "Bar"
user.complete_name = "Foo Bar"
end
- try to mantain your tests small. If, for some reason, you create a helper method that automates some task, create a custom matcher for it too. For example, supose you create a helper that shows error message boxes. So don't do this:
visit something_that_will_raise_an_error_path
within "div#errorMessageBox" do
within "div.errorTitle" do
page.should have_content("Critical Error!")
end
within "div.errorMessage" do
page.should have_content("No space on device!")
end
end
Instead, do this:
visit something_that_will_raise_an_error_path
page.should have_a_message_box(:title => "Critical Error!", :body => "No space on device!")
\#or
page.should have_a_message_box("Critical Error!").with_text("No space on device!")
- exception handling must be made on a specific method
def update_file
file = open_file
return if file.nil?
file.puts "Updated!"
end
def open_file
File.open("/tmp/something", "w")
rescue
return nil
end