Vinícius Bail Alonso
Kent Beck
TDD não é sobre testes
é sobre design
class Person
def say_hello
"Hello!"
end
end
require 'spec_helper'
require_relative '../app/person'
describe Person do
it '#say_hello' do
person = Person.new
expect(person.say_hello).to eq('Hello!')
end
end
examplos/app/person.rb
examplos/spec/person_spec.rb
class Field < ApplicationRecord
scope :get_ordened, -> { select(:name, :id, :area).order(:name) }
end
describe ".get_ordened" do
it 'return fields ordened' do
factories = %i(soy_field aba_field)
culture = FactoryGirl.create(:soy)
factories.each { |f| FactoryGirl.create(f, culture: culture) }
ordened = described_class.get_ordened
expect(ordened.first.name).to eq("Abacate Talhão")
expect(ordened.last.name).to eq("Soja Talhão")
end
end
require 'rails_helper'
feature 'User signs in' do
given!(:user) { FactoryGirl.create(:user) }
scenario 'with valid credentials' do
visit root_path
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
check 'Remember me'
click_button 'Sign in'
expect(page).to have_content "Welcome back, #{user.first_name}!"
end
end
scenario "User pass his data and create a new account" do
visit "/users/sign_up"
fill_in "user_email", with: "test@hotmail.com"
fill_in "user_password", with: 12345678, match: :prefer_exact
fill_in "user_password_confirmation", with: 12345678, match: :prefer_exact
fill_in "user_name", with: "Juca"
fill_in "user_cpf", with: "357.548.846-05"
fill_in "user_phone", with: "(42) 99900-1122"
click_button "Registrar-se"
expect(current_path).to eq(root_path)
end
Iniciando a aplicação
# Criando um novo diretório para o programa
minicurso@tdd:~$ mkdir palindrome
# Acessando o diretório criado
minicurso@tdd:~$ cd palindrome/
# Iniciando a suite de testes RSpec
minicurso@tdd:~/palindrome$ rspec --init
create .rspec
create spec/spec_helper.rb
# Criando um diretório para o código do programa
minicurso@tdd:~/fizz-buzz$ mkdir app
# Listando os diretórios
minicurso@tdd:~/palindrome$ ls
app spec
describe Person do
describe "#save" do
it "a new Person" do
#setup
person = Person.new params
#exercise
person.save
#verify
expect(person.new_record?).to be true
end
#teardown
after do
Person.destroy_all
end
end
end
describe "#total_value" do
context 'with 2 items' do
it 'the sum should be 3500' do
laptop = instance_double('Product', name: 'Notebook', price: 3000)
item_laptop = instance_double('Item', product: laptop, quantity: 1)
book = instance_double('Product', name: 'Livro', price: 50)
item_book = instance_double('Item', product: book, quantity: 10)
allow(item_laptop).to receive(:total_value).and_return(3000)
allow(item_book).to receive(:total_value).and_return(500)
cart.add_item(item_laptop)
cart.add_item(item_book)
expect(cart.total_value).to eq(3500)
end
end
end
Exemplo utilizando stub
describe "#total_value" do
context 'with 2 items' do
it 'the sum should be 3500' do
laptop = instance_double('Product', name: 'Notebook', price: 3000)
item_laptop = instance_double('Item', product: laptop, quantity: 1)
book = instance_double('Product', name: 'Livro', price: 50)
item_book = instance_double('Item', product: book, quantity: 10)
allow(item_laptop).to receive(:total_value).and_return(3000)
allow(item_book).to receive(:total_value).and_return(500)
cart.add_item(item_laptop)
cart.add_item(item_book)
expect(cart.total_value).to eq(3500)
end
end
end
Exemplo utilizando stub
class Game
# ...
def play
@interval.each do |number|
puts @response.formatted number
end
end
end
describe '#play' do
it 'show a single Fizz' do
game = described_class.new (1..3)
expect(STDOUT).to receive(:puts).with('1')
expect(STDOUT).to receive(:puts).with('2')
expect(STDOUT).to receive(:puts).with('Fizz')
game.play
end
end
$ git clone https://github.com/viniciusalonso/minicurso-tdd-app.git
$ cd minicurso-tdd-app.git/
$ bundle install
$ rails g model Post title content:text
Gerando a classe e a migration para a tabela
$ rails db:migrate
Criando a tabela no banco à partir da migration
$ rails g controller Posts
Gerando o controlador e demais arquivos
describe "GET #index" do
it 'return http success' do
get :index
expect(response).to have_http_status(:success)
end
end
spec/controllers/posts_controller_spec.rb
Failures:
1) PostsController GET #index return http success
Failure/Error: get :index
ActionController::UrlGenerationError:
No route matches {:action=>"index", :controller=>"posts"}
$ rspec spec/controllers/posts_controller_spec.rb
resources :posts, only: :index
config/routes.rb
Failures:
1) PostsController GET #index return http success
Failure/Error: get :index
AbstractController::ActionNotFound:
The action 'index' could not be found for PostsController
$ rspec spec/controllers/posts_controller_spec.rb
resources :posts, only: :index
config/routes.rb
Failures:
1) PostsController GET #index return http success
Failure/Error: get :index
AbstractController::ActionNotFound:
The action 'index' could not be found for PostsController
$ rspec spec/controllers/posts_controller_spec.rb
def index
end
app/controllers/posts_controller.rb
Failures:
1) PostsController GET #index return http success
Failure/Error: get :index
ActionController::UnknownFormat:
PostsController#index is missing a template for this request format and variant.
request.formats: ["text/html"]
request.variant: []
$ rspec spec/controllers/posts_controller_spec.rb
$ rails spec/controllers/posts_controller_spec.rb
$ touch app/views/posts/index.html.erb
# Adicionar na linha 19
config.include FactoryGirl::Syntax::Methods
spec/rails_helper.rb
let(:post) { FactoryGirl.create(:post) }
it 'assigns @posts' do
get :index
expect(assigns(:posts)).to include(post)
end
spec/controllers/posts_controller_spec.rb
Failures:
1) PostsController GET #index assigns @posts
Failure/Error: expect(assigns(:posts)).to include(post)
expected nil to include
#<Post id: 1, title: "MyString",
content: "MyText",
created_at: "2017-09-24 23:15:56",
updated_at: "2017-09-24 23:15:56">,
but it does not respond to `include?`
$ rails spec/controllers/posts_controller_spec.rb
def index
@posts = Post.all
end
app/controllers/posts_controller.rb
$ rspec spec/controllers/posts_controller_spec.rb
require "rails_helper"
RSpec.describe "posts/index" do
it "displays all posts" do
assign(:posts, [
FactoryGirl.create(:post, title: 'My title 1'),
FactoryGirl.create(:post, title: 'My title 2'),
FactoryGirl.create(:post, title: 'My title 3')
])
render
expect(rendered).to match /My title 1/
expect(rendered).to match /My title 2/
expect(rendered).to match /My title 3/
end
end
spec/views/posts/index.html.erb_spec.rb
gem 'simple_form'
Gemfile
$ bundle install
Comando para instalar as dependêndias no rails
$ rails g simple_form:install
Instalando a biblioteca no projeto
describe "GET #new" do
it 'instance a new Post' do
get :new
expect(assigns(:post)).to be_a_new(Post)
end
end
spec/controllers/posts_controller_spec.rb
$ rspec spec/controllers/posts_controller_spec.rb
Failures:
1) PostsController GET #new instance a new Post
Failure/Error: get :new
ActionController::UrlGenerationError:
No route matches
{:action=>"new", :controller=>"posts"}
resources :posts, only: [:index, :new]
config/routes.rb
$ rspec spec/controllers/posts_controller_spec.rb
Failures:
1) PostsController GET #new instance a new Post
Failure/Error: get :new
AbstractController::ActionNotFound:
The action 'new' could not be
found for PostsController
def new
@post = Post.new
end
app/controllers/posts_controller.rb
Failures:
1) PostsController GET #new instance a new Post
Failure/Error: get :new
ActionController::UnknownFormat:
PostsController#new is missing a template for this request format and variant.
request.formats: ["text/html"]
request.variant: []
<%= simple_form_for(@post) do |post| %>
<%= f.input :title %>
<%= f.input :content %>
<%= f.button :submit %>
<% end %>
spec/views/posts/index.html.erb_spec.rb
$ rspec spec/controllers/posts_controller_spec.rb
describe "POST #create" do
it 'create new Post' do
expect do
post :create,
params: {post: {title: 'My post',
content: 'My content'}}
end.to change{ Post.count }.by(1)
end
end
spec/controllers/posts_controller_spec.rb
$ rspec spec/controllers/posts_controller_spec.rb
Failures:
1) PostsController POST #create create new Post
Failure/Error:
post :create,
params: {title: 'My post', content: 'My content'}
ActionController::UrlGenerationError:
No route matches {:action=>"create", :content=>"My content",
:controller=>"posts", :title=>"My post"}
resources :posts, only: [:index, :new, :create]
config/routes.rb
$ rspec spec/controllers/posts_controller_spec.rb
Failures:
1) PostsController POST #create create new Post
Failure/Error:
expect do
post :create, params: {title: 'My post', content: 'My content'}
end.to change{ Post.count }.by(1)
expected `Post.count` to have changed by 1, but was changed by 0
def create
@post = Post.new post_params
@post.save
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :content)
end
app/controllers/posts_controller.rb
$ rspec spec/controllers/posts_controller_spec.rb
describe "GET #edit" do
let(:post) { FactoryGirl.create(:post) }
it 'assigns @post' do
get :edit, params: { id: post.id }
expect(assigns(:post)).to eq(post)
end
end
spec/controllers/posts_controller_spec.rb
$ rspec spec/controllers/posts_controller_spec.rb
Failures:
1) PostsController GET #edit assigns @post
Failure/Error: get :edit, params: { id: post.id }
ActionController::UrlGenerationError:
No route matches
{:action=>"edit", :controller=>"posts", :id=>1}
resources :posts, only: [:index, :new, :create, :edit]
config/routes.rb
$ rspec spec/controllers/posts_controller_spec.rb
Failures:
1) PostsController GET #edit assigns @post
Failure/Error:
get :edit, params: { id: post.id }
AbstractController::ActionNotFound:
The action 'edit' could
not be found for PostsController
def edit
@post = Post.find(params[:id])
end
app/controllers/posts_controller.rb
Failures:
1) PostsController GET #edit assigns @post
Failure/Error: get :edit, params: { id: post.id }
ActionController::UnknownFormat:
PostsController#edit is missing a template for this request format and variant.
request.formats: ["text/html"]
request.variant: []
Failures:
1) PostsController GET #edit assigns @post
Failure/Error: get :edit, params: { id: post.id }
ActionController::UnknownFormat:
PostsController#edit is missing a
template for this request format and variant.
request.formats: ["text/html"]
request.variant: []
describe "PATCH #update" do
let(:post) { FactoryGirl.create(:post) }
it 'update post' do
params = { id: post.id, post: { title: 'Meu novo título' } }
patch :update, params: params
post.reload.title
expect(post.title).to eq('Meu novo título')
end
end
spec/controllers/posts_controller_spec.rb
$ rspec spec/controllers/posts_controller_spec.rb
Failures:
1) PostsController PATCH #update update post
Failure/Error: patch :update, params: params
ActionController::UrlGenerationError:
No route matches
{:action=>"update", :controller=>"posts",
:post=>{:id=>1, :title=>"Meu novo título"}}
resources :posts, except: :destroy
config/routes.rb
$ rspec spec/controllers/posts_controller_spec.rb
Failures:
1) PostsController PATCH #update update post
Failure/Error: put :update, params: params
AbstractController::ActionNotFound:
The action 'update' could not be found for PostsController
def update
@post = Post.find(params[:id])
@post.update(post_params)
redirect_to posts_path
end
app/controllers/posts_controller.rb
$ rspec spec/controllers/posts_controller_spec.rb
describe "DELETE #destroy" do
it 'delete post' do
attrs = FactoryGirl.attributes_for(:post)
post = Post.create(attrs)
expect do
delete :destroy, params: { id: post.id }
end.to change{ Post.count }.by(-1)
end
end
spec/controllers/posts_controller_spec.rb
$ rspec spec/controllers/posts_controller_spec.rb
Failures:
1) PostsController DELETE #destroy delete post
Failure/Error: delete :destroy, params: { id: post.id }
ActionController::UrlGenerationError:
No route matches {:action=>"destroy", :controller=>"posts", :id=>1}
resources :posts
config/routes.rb
$ rspec spec/controllers/posts_controller_spec.rb
Failures:
1) PostsController DELETE #destroy delete post
Failure/Error: delete :destroy, params: { id: post.id }
AbstractController::ActionNotFound:
The action 'destroy' could not be found for PostsController
def destroy
@post = Post.find(params[:id])
@post.destroy
redirect_to posts_path
end
app/controllers/posts_controller.rb
$ rspec spec/controllers/posts_controller_spec.rb
describe "GET #show" do
let(:post) { FactoryGirl.create(:post) }
it 'assigns @post' do
get :show, params: { id: post.id }
expect(assigns(:post)).to eq(post)
end
end
spec/controllers/posts_controller_spec.rb
$ rspec spec/controllers/posts_controller_spec.rb
def show
@post = Post.find(params[:id])
end
app/controllers/posts_controller.rb
$ rspec spec/controllers/posts_controller_spec.rb
<strong>
<%= @post.title %>
</strong>
<p>
<%= @post.content %>
</p>
<%= link_to 'Voltar', posts_path %>
app/views/posts/show.html.erb
$ rspec spec/controllers/posts_controller_spec.rb
Coverage report generated for RSpec to
/home/vinicius/Projects/minicurso-app/coverage. 39 / 39 LOC (100.0%) covered.
$ rspec
coverage/index.html
#Adicionar nas linhas 15 e 16
config.include(Shoulda::Matchers::ActiveModel, type: :model)
config.include(Shoulda::Matchers::ActiveRecord, type: :model)
spec/rails_helper.rb
require 'rails_helper'
RSpec.describe Post, type: :model do
context 'validations' do
it { should validate_presence_of(:title) }
it { should validate_uniqueness_of(:title) }
it { should validate_presence_of(:content) }
end
end
spec/models/post_spec.rb
$ rspec spec/models/post_spec.rb
class Post < ApplicationRecord
validates_presence_of :title, :content
validates_uniqueness_of :title
end
app/models/post.rb
$ rspec spec/models/post_spec.rb
require 'rails_helper'
RSpec.feature "HomePages", type: :feature do
scenario "Visit the home page and see messages" do
visit '/'
expect(page).to have_title "Seja Bem Vindo (a)!"
expect(page).to have_text("Bem Vindo(a)!")
end
end
spec/features/home_pages_spec.rb
$ rspec spec/features/home_pages_spec.rb
class WelcomeController < ApplicationController
def index
end
end
app/controllers/welcome_controller.rb
$ rspec spec/features/home_pages_spec.rb
root 'welcome#index'
config/routes.rb
<!-- Linha 4 -->
<title>Seja Bem Vindo (a)!</title>
app/views/layouts/application.html.erb
$ rspec spec/features/home_pages_spec.rb
<h1>Bem Vindo(a)!</h1>
app/views/welcome/index.html.erb
scenario "Go from home page to posts page" do
visit '/'
click_on 'Posts'
expect(page.current_path).to eq(posts_path)
end
spec/features/home_pages_spec.rb
$ rspec spec/features/home_pages_spec.rb
<%= link_to 'Posts', posts_path %>
app/views/welcome.html.erb
$ rspec spec/features/home_pages_spec.rb
gem 'devise'
Gemfile
$ bundle install
$ rails generate devise:install
Instalando dependência
Instalando devise no projeto
$ rails generate devise User
$ rails db:migrate
Gerando model User
Criando tabela users no banco de dados
#Linha 7
require 'devise'
#Linhas 15 e 16
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::IntegrationHelpers, type: :feature
spec/rails_helper.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
# Adicionar linha abaixo
before_action :authenticate_user!
end
app/controllers/application_controller.rb
$ rspec
FactoryGirl.define do
factory :user do
email { Faker::Internet.email }
password "password"
password_confirmation "password"
end
end
spec/factories/users.rb
before(:each) do
sign_in FactoryGirl.create(:user)
end
spec/controllers/posts_controller_spec.rb
$ rspec
spec/controllers/welcome_controller_spec.rb
spec/features/home_pages_spec.rb
$ rails g rspec:feature user_register_himself
scenario "user pass the correct data" do
visit '/users/sign_up'
fill_in "user_email", with: "test@hotmail.com"
fill_in "user_password", with: 12345678, match: :prefer_exact
fill_in "user_password_confirmation", with: 12345678, match: :prefer_exact
click_on "Sign up"
expect(page).to have_text('Bem Vindo(a)!')
end
spec/features/user_register_himselves_spec.rb
scenario "user submit form with a blank email" do
visit '/users/sign_up'
fill_in "user_email", with: ""
fill_in "user_password", with: 12345678, match: :prefer_exact
fill_in "user_password_confirmation", with: 12345678, match: :prefer_exact
click_on "Sign up"
expect(page).to have_text("Email can't be blank")
end
spec/features/user_register_himselves_spec.rb
vba321@hotmail.com