Seguindo nosso CRUD, vamos trabalhar nesse post, a alteração de dados (Update).
Primeiramente crie o arquivo para testar o processo de alteração. Em spec/features/products
crie mais um arquivo nomeado editing_products_spec.rb. Nesse arquivo adicione
o seguinte código:
spec/features/products/editing_products_spec.rb
123456789101112
require'rails_helper'feature'Editando Produtos'dobeforedovisitroot_pathclick_link'Produtos'endscenario"posso editar um produto"doclick_link"Editar"endend
Rode os testes e um erro devido a falta do link “Editar” será apresentado pelo rspec.
Failure/Error: click_link "Editar" Capybara::ElementNotFound:
Unable to find link "Editar"
Precisamos adicionar o link de edição a listagem de produtos. Vamos lá, no arquivo de
template app/views/products/index.html.erb adicione na listagem de dados, o link de edição:
Mas somente isso não é suficiente, precisamos usar o FactoryGirl novamente, para ao menos
termos um registro e assim a nossa listagem ter o link. Ao teste adicione no início
do bloco before:
Rodando novamente os testes, teremos um erro pela falta do nosso template de edição.
Adicione um arquivo de template em app/views/products/edit.html.erb. Agora nosso
teste está passando. Ou seja, nossa aplicação já tem um página de edição. Nosso próximo
passo e exibir um formulário com os dados e alterá-los.
Carregando os dados no formulário de edição
Editando os dados
Vamos començar pelo nosso teste, onde vamos preencher o campo nome do produto, na
verdade vamos modificar o valor que deverá estar carregado. Adicione ao teste:
spec/features/products/editing_product_spec.rb
1234567
scenario"posso editar um produto"doclick_link"Editar"fill_in'Nome',with:'Produto 1'click_button'Salvar'expect(page).tohave_content('Produto foi editado.')end
Rode o teste. E teremos o error:
Failure/Error: fill_in ‘Nome’, with: ‘Produto 1’
Capybara::ElementNotFound:
Unable to find field "Nome"
Ok, não temos o nosso form e o campo Nome no formulário de edição. Vamos reaproveitar
o formulário, usando o atual formulário de novo cadastro, tudo isso com o uso de
partials. Crie no diretório app/views/products/ o arquivo _form.html.erb.
No arquivo edit.html.erb (crie ele se existir) implemente a chamada do partial:
app/views/products/edit.html.erb
123
<p>Editando</p><%= render 'form' %>
Abra o arquivo new.html.erb e transfira o codigo do formulário para o arquivo _form.html.erb,
não esquecendo de adiocionar a chamada do partial ao formulário.
Obs: Mude o click_button do teste de novo registro para “Salvar”.
Rode o teste rspec spec/features/products/editing_products_spec.rb. Erros, erros novamente…
ok mas isso é devido que temos a váriavel @product e nosso controller ela não existe. Vamos
a implementação no nosso controller. Vamos encontrar o registro que queremos editar, usando
o id que é passado a action edit junto com os params da requisição:
Rode o teste o erro agora é diferente e bem claro. Pois queremos gravar a alteração
e o método de update não existe. Crie ele e adicione o código para realizar a alteração
app/controllers/products_controller.rb
12345678910111213141516
classProductsController<ApplicationController...actions...defedit@product=Product.find(params[:id])enddefupdate@product=Product.find(params[:id])if@product.update(product_params)redirect_to@product,notice:'Produto foi editado.'endend...privatemethods...end
Ao rodarmos o teste, ele vai passar. Agora vamos colocar uma cenário de teste negativo.
Deixaremos o campo nome em branco e vamos submeter, o campo nome é obrigatório, logo
uma mensagem de validação vai ser retornada, impedindo que a alteração aconteça:
spec/features/products/editing_product_spec.rb
12345678910
...outrocenario...scenario"quando nome em branco não posso editar um produto"doclick_link"Editar"fill_in'Nome',with:''click_button'Salvar'expect(page).tohave_content('Produto não foi alterado, verifique os erros.')expect(page).tohave_content('Nome é muito curto (mínimo: 5 caracteres)')end
E para finalizar adicione uma mensagem de alerta para quando ocorrer uma falha:
app/controllers/products_controller.rb
12345678910111213141516171819
classProductsController<ApplicationController...actions...defedit@product=Product.find(params[:id])enddefupdate@product=Product.find(params[:id])if@product.update(product_params)redirect_to@product,notice:'Produto foi editado.'elseflash[:alert]='Produto não foi alterado, verifique os erros.'render:editendend...privatemethods...end
Rode seus testes, e tudo estará verde. Finalizamos mais uma etapa, o código fonte como
sempre estará no meu github. Até..
Vamos começar a segunda parte, que será o carregamento dos dados. Iniciaremos mais
uma vez por nossos testes, então a primeira coisa a fazer é criar o arquivo reading_products_spec.rb
na pasta de features (spec/features/products/reading_products_spec.rb.
Acessando a página
Iniciaremos nosso teste com um cenário onde estaremos navegando até a página,
então vamos ao trabalho. Implemente a navegação a página de listagem:
spec/features/products/reating_products_spec.rb
12345678910
require"rails_helper"feature"Listando Produtos"doscenario"listando todos os registros cadastrados"dovisit'/'click_link'Produtos'expect(page).tohave_selector('h2',text:'Cadastro de produtos')endend
Organizando a navegação
Vamos adicionar um link para a pagina de listagem. No partial que contém os links
de navegação (app/views/layouts/_navigation_links.html.erb) substitua todo o conteúdo por:
app/views/layouts/_navigation_links.html.erb
1
<li><%=link_to'Produtos',products_path%></li>
Rode o teste com o comando rspec spec/features/products/reading_products_spec.rb,
teremos um erro, pois ainda não criamos nossa ação index em nosso controlador de
produtos:
Erro por falta da ação index no controlador
123456
Failures:
1) Listando Produtos listando todos os registros cadastrados
Failure/Error: visit products_path
AbstractController::ActionNotFound:
The action 'index' could not be found for ProductsController
A resolução desse erro é, criar uma ação index no nosso controlador:
Rodar os testes novamente irá retornar um erro devido a falta do template para
essa ação:
Erro pela falta de template
123456
Failures:
1) Listando Produtos listando todos os registros cadastrados
Failure/Error: visit products_path
ActionView::MissingTemplate:
Missing template products/index, application/index with {:locale=>[:"pt-BR"], :formats=>[:html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in:
Crie o template app/views/products/index.html.erb, com o conteudo:
Adicione o conteudo app/views/products/index.html.erb
1
<h2>Cadastrodeprodutos</h2>
Rode o teste novamente e ele irá passar.
Carregando os dados
Na página criada vamos carregar os dados. Crie um bloco before, onde estarão os
trechos de testes que se repetirão. O before já foi utilizado no post anterior,
mas somente para lembrar…. o bloco before deve ser usado sempre que se precisa
que algo seja carregado antes dos testes, assim como é o nosso caso:
spec/features/products/reading_products_spec.rb
123456789101112
require"rails_helper"feature"Listando Produtos"dobeforedovisitroot_pathendscenario"listando todos os registros cadastrados"doclick_link'Produtos'endend
Agora vamos implementar nosso cenário de carregamento dos dados. Vamos verificar
se a nossa listagem possui ao menos um registro. Para isso vamos carregar nossa
factory e então verificar se há ao menos um registro:
Ao menos um registro deve existir spec/features/products/reading_products_spec.rb
12345678
scenario"listando todos os registros cadastrados"do@products=FactoryGirl.create_list(:product,25)click_link'Produtos'expect(page).tohave_content(@products.first.name)expect(page).tohave_content(@products.last.name)end
Aqui entra o FactoryGirl, que para quem não conhece é uma gem para facilitar a criação
de fixtures e factories, evitando o uso direto do Active Record. Vamos usar para gerar
uma lista de 25 produtos, assim testando a exibição.
Primeiro vamos adiocionar a gem ao projeto, então em seu arquivo Gemfile, abaixo das demais gem’s do grupo
de development e test, adicione, salve e em seguida rode o comando bundle install:
Instalada a gem, crie a factory para produtos. No diretorio spec crie uma pasta
chamada factories e nela um arquivo com nomeado products.rb. Essa factory
vai mapear o modelo Product, assim tudo que é referente a manipulação de modelos
em nossos testes, serão de responsabilidade de nossa factory. Adicione os campos
necessários para nosso teste:
Products spec/factories/products.rb
123456
FactoryGirl.definedofactory:productdosequence(:name){|n|"Produto #{n}"}description'Descrição do produto 1 (um)'endend
Perceba o uso do sequence na factory, ele está é usado para nunca termos nomes
iguais. Veja mais sobre o Factory Girl aqui: site
Nosso teste se executado nesse ponto, estará quebrado, pois estamos esperando um valor
que ainda não está sendo apresentado na nossa página. Abaixo vamos implementar a exibição
dos resultados.
Adicione na acao index do controlador o seguinte código, que carregará os dados cadastrados:
Carregando os dados app/controllers/products_controller.rb
Rode o teste rspec spec/features/reading_products_spec.rb e nossa listagem deverá funcionar.
Caso quira inicie o server rails (rails s) e veja o resultado.
Ultímos ajustes
Primeiramente rode todos os testes, para garantir que tudo está funcionando ou achar ocasionais
quebras. Execute o comando rspec e bingo, temos uma quebra. Ela ocorre devida a
modificação do nosso arquivo de navegação, onde removemos o link para o formulário de
cadastro. Para solucionar isso devemos mudar a forma como o Capybara navegava até o formulário
e principalmente definir um link na página de listagem.
Na spec de cadastro (spec/features/creating_products_spec.rb) altere o bloco before
para a navegação desejada:
Ao rodar o nosso teste, ainda permanece o mesmo erro, sendo a solução adicionar esse link
em nosso template index (app/views/products/index.html.erb). Então a baixo da tag de
fechamento da tabela adicione o link:
app/views/products/index.html.erb
1234567
<h2>Cadastro de produtos</h2> <table class="table table-hover"> .... </table><%=link_to"Novo Produto",new_product_path,class:"btn btn-primary"%>
Agora todos os testes estão verdes. Para finalizar vamos adicionar um teste para quando
não houver dados a serem retornados. Ao teste de carregamento adicione o cenário:
spec/features/reading_products_spec.rb
1234
scenario"exibindo mensagem de que nao ha dados"doclick_link'Produtos'expect(page).tohave_content('Nenhum registro encontrado!')end
E ao nosso template basta colocar a validação para quando a listagem estiver vazia.
Nosso template será dessa forma:
Agora sim chegamos ao final. Desenvolvemos o carregamento dos dados cadastrados usando
testes, com Cabybara, Rspec e FactoryGirl. No próximo post vamos ver como alterar nossos
registros e como excluí-los. O código fonte desse post está no meu github, sendo separado por branches.
O acrônimo CRUD (Create, Read, Update e Delete) é algo que todo iniciante em linguagens de programação ou frameworks necessita saber, pois significa nada menos que as operações básicas para a criação/manutenção de cadastros.
O nosso primeiro passo é a parte de criação (C do CRUD). Para realizar todos os passos vamos desenvolver usando as metodologias de BDD/TDD, que irá nos possibilitar um código estável e mais fácil de manter.
Criando um cadastro
Nessa primeira história vamos criar um resource (recurso) que representa um produto, é somente para fins didáticos, o nome é o que menos interessa. Para o produto vamos ter dois atributos que são necessários: nome e descrição. O nosso primeiro passo é criar a nossa feature de spec, em spec/features (crie esse diretorios se não existirem), crie o diretório products e um arquivo com o nome de creating_products_spec.rb.
Uma dica, sempre use a nomenclatura em inglês em sua aplicação Rails, garanto que você evitará muitas dores de cabeça, pois o Rails se torna muito mais amigável na língua do tio Obama.
No arquivo criado vamos especificar o que desejamos que o nosso cadastro faça. Abaixo está o código da especificação:
spec/features/products/creating_products_spec.rb
123456789101112131415
require‘rails_helper’feature'Criando Produtos'doscenario"posso criar um produto"dovisit'/'click_link'Novo Produto'fill_in'Nome',with:'Produto 1'fill_in'Descrição',with:'Descrição do produto 1 (um)'click_button'Criar produto'expect(page).tohave_content('Produto foi criado.')endend
Execute o comando rspec spec/features/products/creating_products_spec.rb. Esse comando executará a nossa feature de teste e que apresentará o seguinte erro:
Erro no bash
123456
Failures:
1) Criando Produtos posso criar um produto
Failure/Error: click_link 'Novo Produto' Capybara::ElementNotFound:
Unable to find link "Novo Produto"
O teste falhará pois não temos o link “Novo Produto” na rota “/”, que é a nosso root path(página inicial). Então antes de seguir, vamos criar o link na página inicial. Coloque o seguinte código dentro do arquivo _navigation_links.html.erb, que está na pasta layouts de nossas views (app/views/layouts/_navigation_links.html.erb):
Rode os testes novamente e ocorrera um erro pois não definimos uma rota para os nossos produtos:
Erro no bash
123
Failure/Error: visit '/' ActionView::Template::Error:
undefined local variable or method 'new_product_path'
Adicione a rota no arquivo config/routes.rb:
config/routes.rb
1
resources:products
Rode os testes novamente e acontecerá um nova quebra na nossa feature. Dessa vez é devido que a nossa suite de testes até conseguiu encontrar a rota, mas não encontrou nenhum controller que condize-se com o resource procurado. Veja o erro:
Essa ficou fácil. Crie o controller, em app/controllers/products_controller.rb. Nesse arquivo declare um classe ruby com o nome de ProductsController extendendo de ApplicationController:
app/controllers/products_controller.rb
123
classProductsController<ApplicationControllerend
Rode novamente o teste. Problema resolvido, mas outro surgiu. Agora o rspec não conseguiu encontrar uma action no controller, que refletisse com a operação de novo cadastro. Veja:
Ação ainda não existe
123
Failure/Error: click_link 'Novo Produto' AbstractController::ActionNotFound:
The action 'new' could not be found for ProductsController
Ao controller criado instantes atrás, adicione a action new:
Devemos criar o nosso template na camada de visão do projeto. Então na pasta app/views, crie uma pasta chamada products e nela um arquivo com o nome new.html.erb. Feito isso rode o teste mais uma vez. Agora temos um problema diferente de todos até agora. O Capybara na tentativa de preencher os campos do formulário (que ainda não existe) acaba gerando um novo erro:
Não encontrou o elemento Nome
123
Failure/Error: fill_in 'Nome', with: 'Produto 1' Capybara::ElementNotFound:
Unable to find field "Nome"
Como podemos ver, o Capybara não conseguiu encontrar o elemento com o field “Nome”. Isso é lógico, nós não criamos ainda o formulário com os campos. Porém antes teremos que criar uma instância de Product em nossa controller e passar a nossa view para que ela possua os campos necessários:
Implementação da action new
123
defnew@product=Product.newend
A constante Product é o nosso model, que deve ser criado em app/models/product.rb. Mas antes de sair criando o arquivo, vamos acelerar as coisas usando um gerador de código do Rails.
Comando para gerar tudo relacionado ao model
1
rails g model Pproduct name:string description:string
Esse generator (gerador) nos poupa um tempo criando uma série de arquivos, como o arquivo de migração do banco de dados com os campos necessários, nosso model extendendo de ActiveRecord que contém um infinidade de funcionalidades para trabalhar com nossos dados e arquivos de testes unitários.
Um Model deve ser capaz de prover o acesso a camada de dados e é por isso que ele utiliza o Active Record. O Model também provê um local para a lógica de negócio, bem como, fazer validações e associações entre modelos.
Entre os arquivos gerados, está o arquivo de migration. Migration é um mecanismo eficiente de manter um controle de versão de nosso banco de dados, onde podemos evoluir e retrocer nosso schema quando necessário, sem trabalho adicional. Nosso arquivo de migração está é gerado dentro da pasta db/migrate e o nome leva em considereção o timestamp do momento de geração, para nunca ter o mesmo nome e o Rails conseguir controlar o historico de migrações. Nosso arquivo db/migrate/[data]_create_product.rb apresenta o seguite código Ruby:
Devemos rodar o comando abaixo para ter o nosso banco de dados atualizado:
Comando para atualizar nosso banco de dados
1
rake db:migrate
O esquema de nomenclatura é interessante, o Rails irá ter o model com o nome product (singular), no entanto a tabela no banco de dados será products(plural).
Com nosso model product criado e o nosso banco com a tabela necessária, vamos voltar ao formulário. Execute o teste novamente:
E veremos que nada mudou desde a ultima execução, porém agora temos nosso banco de dados pronto, e um objeto do tipo Product que dará os atributos necessários para montar nosso formulário de cadastro. Abra o arquivo app/views/products/new.html.erb e adicione o código abaixo:
Formulário criado, devemos rodar nosso teste novamente. Fazendo isso veremos que nesse ponto, o que ocorre é que o formulário é preenchido, porém ao ser submitido, nenhuma ação para criar (create) é encontrado no nosso controller:
A ação create não esta definida
123
Failure/Error: click_button 'Criar produto'AbstractController::ActionNotFound:
The action 'create' could not be found for ProductsController
Então para resolver isso devemos criar o nosso método create dentro do ProductContoller:
Definindo a action create
1234567891011
defcreate@product=Product.new(product_params)if@product.saveredirect_to@product,notice:"Produto foi criado."elseflash[:alert]="Produto não pode ser criado."render"new"endend
Também defina um método privado para permitir que nossos parametros sejam lidos, sem serem bloqueados pelo mecanismo de Strong Parameters do Rails 4:
Como vamos usar flash messages do Rails, é necessário em nosso application.html.erb definir um local para
fazer a exibição das mensagens. Crie um diretório denominado shared dentro da pasta views. Nessa nova pasta
crie o arquivo _messages.html.erb. Adicione o seguinte conteúdo:
Rode seu teste e ele ira dizer que não encontrou a action show. Isso quer dizer que o nosso cadastro está sendo salvo, porém não temos uma ação para fazer a exibição de cadastro. Adicione a ação show ao seu ProductController:
Nossa ação para busca e exibir o usuário
123
defshow@product=Product.find(params[:id])end
Se rodarmos os testes vamos ter um erro por que o template correspondente a action, não pode ser encontrada, crie um o seguinte arquivo app/views/products/show.html.erb, e adicione o código abaixo:
Rodandos os testes, nossa feature vai passar. Se quiser testar manualmente, inicie o servidor com o comando rails s e realize um cadastro.
Adicionando validações aos nossos campos
Como o nosso cadastro criado e funcionando, devemos adicionar algumas validações
para os nossos campos. Ao campo nome vamos verificar se ele foi informado,
se atende o tamanho minímo de 3 caracteres e o máximo de 50 e também, o nome deve
ser único. Já para o campo descrição verificáremos se foi informado e se possue
pelo menos 15 carácteres de texto. Nessa etapa, vamos usar testes unitários e testes
de integração. Os testes unitários vão garantir que as validações estão sendo exigidas
e o teste integrado deverá validar a operação de cadastro completo.
Testando unitáriamente nosso model
Vamos iniciar testando no nosso model Product se algum valor a propriedade name foi
informado, assim impediremos que o nosso campo tenha um valor em branco. Vamos usar
a validação de tamanho para impedir o não preenchimento do campo. Adicione o seguinte código de ao arquivo:
spec/models/product_spec.rb
1234567891011121314151617181920212223
require'rails_helper'describeProductdobeforedo@product=Product.new(name:"Cadastro Exemplo",description:"Descrição do cadastro Exemplo")enddescribe"quando o nome não foi informado"dobefore{@product.name=""}it{should_notbe_valid}enddescribe"quando o nome é muito curto"dobefore{@product.name="na"}it{should_notbe_valid}enddescribe"quando o nome é muito longo"dobefore{@product.name="n"*50}it{should_notbe_valid}endend
Rode o comando rspec spec/models/product_spec.rb e o nosso teste deverá falhar,
pois estamos esperando que algo errado aconteça com o uso dos métodos should_not be_valid,
mas como não definimos as validações no nosso model, nada de errado ocorre e o nosso teste
falha. Adicione o código abaixo e rode o comando em seguida, nosso testes deve passar:
A validação do nome está quase completa, para finalizar adicione ao teste á baixo do último
bloco describe:
spec/models/product_spec.rb
123456789
describe"quando o nome de produto já está sendo usado"dobeforedoproduct_with_same_name=@product.dupproduct_with_same_name.name=@product.nameproduct_with_same_name.saveendit{should_notbe_valid}end
Usamos o método dup quando queremos duplicar um módelo. Aqui queremos que o nome que já
exista, não seja duplicado, e é isso que o teste nos assegura. Ao model adicione:
app/models/product.rb
1
validates_uniqueness_of:name
E rode o teste novamente. Tudo verde, o campo nome está validado, agora vamos
validar o campo description. Ao teste insira abaixo do ultimo bloco de describe
o seguinte código:
spec/models/product_spec.rb
1234567891011121314
describe"quando a descrição não foi informada "dobefore{@product.description=""}it{should_notbe_valid}enddescribe"quando a descrição é muito curta"dobefore{@product.name="n"*15}it{should_notbe_valid}enddescribe"quando a descrição é muito longa"dobefore{@product.name="n"*255}it{should_notbe_valid}end
Mas que beleza! Nosso modelo está validando nossos dados e temos a garantia disso
com os testes unitários, hora de testar integrado e ajustar nossas mensagens.
Testando de maneira integrada
Antes de continuar a implementação, vamos refatorar a nossa spec feature spec/features/products/creating_products_spec.rb. Vamos adicionar um bloco before,
e nele vamos colocar o que estará repetindo nos outros cenários, nossa feature deve ser
semelhante a isso:
spec/features/products/creating_products_spec.rb
12345678910111213141516
require'rails_helper'feature'Criando Produtos'dobeforedovisit'/'click_link'Novo Produto'endscenario"posso criar um produto"dofill_in'Nome',with:'Produto 1'fill_in'Descrição',with:'Produto 1 (um)'click_button'Criar produto'expect(page).tohave_content('Produto foi criado.')endend
Rode a spec com o comando rspec spec/features/products/creating_products_spec.rb, seu primeiro
cenário deverá falhar. Agora estamos validando nossos campos, e a nossa descrição não está
no tamanho correto, corrija, colocando uma descrição com pelo menos 15 caracteres, rode o teste
novamente e tudo estará correto.
Vamos agora focar nos cenários de teste negativo. Primeiro para valores do campo nome.
Crie um novo cenário abaixo do último criado, como o seguinte código:
insira em spec/features/products/creating_products_spec.rb
1234567
scenario"com nome inválido não posso criar um produto"dofill_in'Nome',with:''fill_in'Descrição',with:'Produto com uma bela descrição de teste'click_button'Criar produto'expect(page).tohave_content('Nome é muito curto (mínimo: 5 caracteres)')end
Se rodar o teste, irá falhar, pois essa mensagem não está sendo exibida. Vamos criar
um arquivo, na pasta shared de nossas views, com o nome de errors.html.erb
(app/views/shared/errors.html.erb). Será um partial, que irá exibir todos os
erros de validação.
insira em app/views/shared/_errors.html.erb
123456789101112
<% if object.errors.any?%> <div id="error_explanation"><divclass="alert alert-error">Oformuláriocontém<%= pluralize(object.errors.count, "erro") %>. </div> <ul> <% object.errors.full_messages.each do |msg| %> <li>* <%=msg%></li><% end %> </ul></div> <% end %>
Também deve-se incluir esse arquivo no formulário de cadastro:
Podemos rodar nosso teste novamente, e teremos ainda algum erro, mas estamos quase.
Precisamos ajustar para que nossos atributos (name e description) sejam reconhecidos
como nome e descrição. Basta mudar no nosso arquivo de tradução em config/locales/pt-BR/pt-BR.yml
Dessa maneira podemos traduzir tudo que o active record gera automáticamente, é uma maneira
muito flexível de se traduzir o nosso sistema. Rode novamente os testes e dessa vez, nossa
spec passou. Hora de refatorar. Agora que colocamos a tradução dos campos do active record,
não precisamos mais a tradução que está fixa nos campos do formulário de cadastro. Remova
a tradução:
remova a tradução em app/views/products/new.html.erb
insira em spec/features/products/creating_products_spec.rb
1234567
scenario"com descrição inválida não posso criar um produto"dofill_in'Nome',with:'Meu produto'fill_in'Descrição',with:''click_button'Criar produto'expect(page).tohave_content('Descrição é muito curto (mínimo: 15 caracteres)')end
Poderíamos colocar mais testes, mas não é esse o intuíto do post (Fica de incentivo para você).
O código fonte pode ser baixado aqui: https://github.com/marceloboth/crud-rspec. Baixando o código
faça checkout para o branch criando_cadastro: git chechout criando_cadastro.
Esse post é o primeiro de uma série que irão servir como um próximo passo a quem estiver
iniciando com Ruby on Rails, algo além de “construa um blog em quinze minutos” e
sim algo mais útil ao mundo real.
Nesse post veremos sobre como gerar nossa aplicação, configurar nossos testes,
instalar uma biblioteca de front-end e deixar tudo pronto para iniciar o desenvolvimento.
Criando nossa aplicação
Criar a aplicação é um passo simples, onde usaremos os geradores do Rails para
fazer o trabalho árduo de montar a estrutura e deixar tudo configurado, baseado
no conceito de “Convenção sobre configuração”. No terminal, navege até o diretório
que deseja criar o projeto e digite o seguinte comando:
Criar o projeto Rails
1
rails new crud-rspec -T
O comando rails new com o argumento -T, é para sinalizar ao gerador, que não queremos
o conjunto de testes padrão do Rails, já que vamos usar o Rspec. Para ver mais opções
de configuração utilize o help do comando: rails new -h
Aguarde a finalização da construção…
Saida da linha de comando
123456789101112131415161718
rails new crud-rspec -T
create
create README.rdoc
create Rakefile
create config.ru
create .gitignore
create Gemfile
create app
create app/assets/javascripts/application.js
create app/assets/stylesheets/application.css
..................
..................
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
run bundle exec spring binstub --all
* bin/rake: spring inserted
* bin/rails: spring inserted
No seu bash ou outra ferramenta de linha de comando (recomendo zsh), navegue para
o diretório que foi criado usando cd crud-rspec. Abra o diretório com o seu editor
favorito.
O framework Rails nos impõe algumas convenções e uma estrutura inicial para seguir.
Primeiramente temos o uso de uma arquitetura MVC onde temos nos models, que manipulam
nossos dados, controllers que gerenciam o que deve ser executado e para onde entregar
as informações e nossas views que fazem a interação, recebem as entradas e mostram
as saidas ao usuário. Isso tudo está na pasta app do projeto. Esse é o local onde
passaremos a maior parte do nosso desenvolvimento. Abaixo uma descrição básica de
todos os diretórios criados.
app: Local do código principal da aplicação. Models, Controllers, Views, Helpers,
CSS e código Javascript da nossa aplicação ficam nessa pastas.
bin: Armazena os scripts dos geradores do rails.
config: Configuração do banco de dados, intenacionalizações, especificidades de
algumas gems (Devise,SimpleForm, etc), rotas da aplicação e muitas outras definições.
db: Mantém o esquema e as migrações da base de dados.
lib: Aqui ficam todos os códigos que não são diretamente ligados a aplicação.
São armazenadas as tarefas rake, bem como tasks que são executadas fora do ambiente
web.
log: Arquivos de log, simples assim :).
public: Páginas de erro (404, 500) e arquivos estáticos como por exemplo o favicon.ico
e o robots.txt.
tmp: Guarda o cache e PID da aplicação.
vendor: Local onde são colocadas bibliotecas que não sejam gems.
A construtor de projetos do Rails poupa a nós um bom tempo, pois não precisamos nos preocupar
com a arquitetura inicial do projeto, pois ele que cria as pastas e arquivos, de acordo
com a nossas escolhas.
Configurar nossos testes
Vamos iniciar a configuração dos nossos testes. Primeiro passo é instalar a gem
de testes Rspec e a gem para testes de aceitação
Capybara, pra isso devemos adicionar elas ao nosso
arquivo Gemfile, e devemos definir a qual grupo de gems elas pertencem (desenvolvimento, testes ou produção). Como
nossos testes apenas são usados em teste e produção, ficará de seguinte maneira:
veremos que o diretório rspec foi gerado, e os arquivos de configuração também.
Are you ready?
Criamos nosso projeto e também configuramos o rspec, poderiamos implementar nossa
primeira funcionalidade, mas antes, vamos configurar uma biblioteca de front-end, no caso
twitter bootstrap. Para isso adicione a gem do twitter bootstrap em nosso Gemfile:
Adicione ao Gemfile
12
gem'bootstrap-sass'gem'autoprefixer-rails'
Rode novamente o bundle install para instalar a gem e na seqüência configure, da seguinte
maneira:
Renomeie o arquivo app/assets/stylesheets/application.css para app/assets/stylesheets/application.css.scss
Adicione ao arquivo application.css.scss:
12
@import"bootstrap-sprockets";@import"bootstrap";
Adicione ao app/assets/javascript/application.js //= require bootstrap-sprockets
Crie um controller com uma view para servir como o Index de nossa Web Application:
Crie o controlle e a view
1
rails g controller home index
Isso criará uma série de arquivos, que são: o nosso controller, nossa view, arquivos
de css e javascript, testes e também é feita uma alteração em nosso arquivo de rotas:
Agora que temos nossa estrutura gerada, nossos testes configurados e nosso front-end
pronto, vamos definir a nossa página principal. Nosso primeiro passo é definir que
a nossa rota raiz da aplicação seja o index do controlador home. Com isso feito vamos
colocar um menu superior na página e um link para o cadastro de produtos que será implementado
no próximo post. Crie um diretório denominado features dentro do diretório spec,
e dentro da pasta criada crie um arquivo chamado reading_products_spec.rb.
Nesse arquivo implemente o teste da seguinte maneira:
Escrevendo nosso teste spec/features/reading_products_spec.rb
1234567
require"rails_helper"feature"Listando Produtos"doit"consigo acessar o link da página"dovisitroot_pathendend
Antes de rodar o teste, desabilite o lançamento de warnings do rspec. Faça isso
removendo a linha que contém o conteúdo --warnings do aquivo .rspec que está na raiz do projeto.
Agora rode o teste com o comando rspec spec/features/reading_products_spec.rb. Um erro
ocorrerá:
Erro ocorrido
1234
1) Listando Produtos consigo acessar o link da página
Failure/Error: visit root_path
NameError:
undefined local variable or method `root_path'for#<RSpec::ExampleGroups::ListandoProdutos:0xa824e84>
Esse erro acontece pois estamos tentando visitar a raiz de nosso site e ela ainda não
está configurada nas rotas. Abra o arquivo config/routes.rb, e deixe a sua implementação
na seguinte forma:
Salve e rode o teste novamente. Nosso teste passou, vamos a mais uma parte da implementação,
que é onde vamos colocar o cabeçalho e o link para o recurso de listagem de cadastros.
Ao teste adicione:
Encontrando o link de listagem spec/features/reading_products_spec.rb
123456
feature"Listando Produtos"doit"consigo acessar o link da página"dovisitroot_pathexpect(page).tohave_link('Produtos')endend
Rodando nosso teste, teremos uma quebra, pois esse link não existe em nossa página
inicial. Vamos colocar um menu, com o conjunto de estilos do Twitter Bootstrap a
nossa página de layout app/views/layouts/application.html.erb:
Implemente a página de layout app/views/layouts/application.html.erb
Rode os testes novamente, agora tudo vai passar. Temos nossa página inicial definida,
com o uso de testes de integração. O que fizemos foi, adicionar uma rota raiz para
nossa aplicação, colocar um menu e um link para o cadastro de produtos (que está sem caminho),
e tudo isso guiado por testes, usando Rspec e Capybara.
Finalizamos nossa primeira parte, onde configuramos nossa aplicação com algumas
ferramentas que o Rails e o Ruby nos proporcionam para desenvolvermos com Agilidade
sem perder qualidade. Nos próximos posts vamos implementar um Crud usando Rspec, Capybara e Rails.
O código pode ser baixado no Github, somente observe que cada post está
dividido por branch.