23 Feb 2015
Single Assertion principle states:
A test suite should only have one assertion, so that if there is a failure
they fail separately. If you find yourself using "and" then you are probably
testing too much at once:
get :index
assigns(:todos).should == (cook, sleep)
There is an exception to this rule - when the assertions are very related concerning the same object:
get :new
assigns(:todo).should be_new_record
assigns(:todo).should be instance_of(Todo)
20 Feb 2015
An Alternative Style of Rspec
- using let, before, subject, etc.
Uses lazy evaluation which means it doesn't evaluate the :todo
or the subject until it has to ...namingly when it reaches it or before.
To avoid lazy evaluation, you could do:
let!(:todo) {Todo.create(name: "cook dinner")}
Pros:
is more concise and separates the common set up code using lets
Cons:
For a lengthy test suite it may be hard to connect the test with the description.
Using let
Step 1. Eliminate the repetition of Todo.create in each test:
describe "#display_text" do
let(:todo) { Todo.create(name: "cook dinner") }
Step 2. Replace todo.display_ text
describe "#display_text" do
let(:todo) { Todo.create(name: "cook dinner") }
let(:subject) { todo.display_text }
or
Step3. Replace todo.display_text
describe "#display_text" do
let(:todo) { Todo.create(name: "cook dinner") }
subject { todo.display_text }
Using before for case specific tests
context "displays the name when there's no tags" do
it "displays the name when there's no tags" do
subject.should == "cook dinner"
end
end
context "displays the only tag with word 'tags' when there's one tag"
before do
todo.tags.create(name: "home")
todo.tags.create(name: "urgent")
end
it "displays name with multiple tags" do
subject.should == "cook dinner (tags: home, urgent)"
end
end
Change lengthy context to more succinct:
context "displays the only tag with word 'tags' when there's one tag"
to =>
Remove the "it -text-" because its in context with the description
context "one tag" do
before { todo.tags.create(name: "home") }
it { should == "cook dinner (tag: home)" }
end
18 Feb 2015
Techniques you can use to grow your test suite:
Testing tags (many to many relationship with Todos)
* When testing for numbers, you want to use the 0, 1, many, and boundary_condition
(if there is one)
describe "display_text" do
it "displays the name when there's no tags"
it "displays the only tag with word 'tag' when there's one tag"
it "displays name with multiple tags"
it "displays up to four tags"
end
end
describe "#display_text" do
it "displays the name when there's no tags" do
todo = Todo.create(name: "cook dinner")
todo.display_text.should == "cook dinner"
end
it display the only tag with the word 'tag' when there's one tag do
todo = Todo.create(name: "cook dinner")
todo.tags.create(name: "home")
todo.display_text.should == "cook dinner (tags: home)"
end
it "displays name with multiple tags" do
todo.tags.create(name: "cook dinner")
todo.tags.create(name: "home")
todo.tags.create(name: "urgent")
todo.display_text.should == "cook dinner(tags: home, urgent)"
end
it "displays up to four tags" do
todo = Todo.create(name: "cook dinner")
todo.tags.create(name: "home")
todo.tags.create(name: "urgent")
todo.tags.create(name: "help")
todo.tags.create(name: "book")
todo.tags.create(name: "patience")
todo.displat_text.should == "cook dinner (tags: home, urgent, help, book, more...)"
end
end
16 Feb 2015
Add feature in the Red and refactor in the green.
- Pull out anything complex into a separate method and give it a name
- Clean up public facing methods.. pulling complex code into private methods
Example:
class Todo < ActiveRecord:;Base
has_many :taggings
has_many :tags, through: :taggings
validates_presence_of :name
def name_only?
description.blank?
end
def display_text
name + tag_text
end
private
def tag_text
if tags.any?
" (#{tags.one? ? 'tag' : 'tags' } : #{tags.map(&:name).first(4).join(", ")}
#{', more...' if tags.count > 4})"
else
""
end
end
end
13 Feb 2015
With any test, there are typically 3 actions:
- Set up the data required for the action
- Perform the action
- Verify the the expected result
Controller Tests
- are typically considered more functional bc you're pulling together
multiple components
You typically never have to tests the render or redirect views portion of an action
because that is testing the functionality of rails rather than the code we write. The goal
of testing is to test the code we write only.
You do, however want to test redirect_to for unauthorized users.
Also if you dont wan't to rerun all the tests in your suite and just run one in particular
you can specify "line:27" and it will run the test on that line.
rspec spec/controllers/todos_controller_spec.rb 27
class TodoController < ApplicationController
def index
@todos = Todo.new
end
def new
@todo = Todo.new
end
def create
@todo = Todo.new(params[:todo])
if @todo.save
redirect_to root_path
else
render :new
end
end
end
Steps:
- Create directory: controllers
- Create spec: todosconrollerspec.rb
- Controller typically have two goals... render and redirect
require 'spec_helper'
describe TodoController do
describe "GET index" do
it "set the @todos variable" do
cook = Todo.create(name: "cook")
sleep = Todo.create(name: "sleep")
get :index
assigns(:todos).should == (cook, sleep)
end
it "render the index template" do
get :index
response.should render_template :index
end
end
describe 'GET new' do
it "sets the @todo variable" do
get :new
assigns(:todo).should be_new_record
assigns(:todo).should be_instance_of(Todo)
end
it "renders the new template" do
get :new
response.should render_template :new
end
end
describe "POST create" do
it "creates the todo record when the input is valid" do
post :create, todo: {name: "cook", description: "I like to cook"}
Todo.first.name.should == "cook"
Todo.first.description.should == "I like cooking"
end
it "redirects to the root path when the input is valid" do
post :create, todo: {name: "cook", description: "I like cooking"}
response.should redirect_to root_path
end
it "does not create a todo when the input is invalid" do
post :create, todo: {description: "I like cooking"}
Todo.count.should == 0
end
it "renders the new template when the input is invalid" do
post :create, todo: {description: "I like cooking"}
response.should render_template :new
end
end