Escolar Documentos
Profissional Documentos
Cultura Documentos
Introdução
xemplo:
E
GET -> http://www.traducaoendIP:80/task/21
Verbo -> protocolo://url:porta/path_do_recurso
e por analogia imaginarmos um projeto git como uma árvore, um branch é o análogo a um
S
galho com o objetivo de realizar uma melhor orientação e organização no desenvolvimento da
aplicação. Numa branch pode ser estabelecida quais regras serão implementada e para quem
elas serão destinadas, facilitando na rastreabilidade de uma feature ou até mesmo perceber
métricas no desenvolvimento da mesma.
essa forma sempre que se entender necessário será derivada da linha master uma nova
D
branch para desenvolvimento de uma feature por exemplo e posteriormente se realizar a
inclusão da mesma na master por meio do comando merge.
Para se criar uma branch num projeto git inicializado, basta-se utilizar o comando:
git branch
lém do mais será explorado o conceito de namespace no arquivo de rotas e pastacontrollers
A
para melhor separação/organização da api do restante do projeto, outras configurações default
como mimetype (json), restrições (constraints) e subdomínios:
Isto pode ser realizado ajustando o arquivo de hosts e depende do sistema operacional, p.e.:
:\Windows\System32\drivers\etc\hosts
C
127.0.0.1 api.task-manager.test
> config/environments/development.rb.
Rails.application.configure do
# Whitelist one hostname
config.hosts << "api.task-manager.test" ou
config.hosts.clear
end
para que a aplicação funcione é necessário criar o banco de dados através do
E
comando no terminal:
rails db:create
git add . ; git commit -m “Setting API route file, database.yml and whitelists”
implementação da classe foi realizada na pasta lib, convencionado que nesta pasta, estão
A
incluídas classes que não estejam diretamente relacionadas a regra de negócio da aplicação.
class ApiVersionConstraint
def initialize(options)
@version = options[:version]
@default = options[:default]
end
def matches?(req)
@default ||
req.headers['Accept'].include?("application/vnd.task-manager.v#{@version}")
end
end
opção de se incluir uma hash como parâmetro de inicialização deve-se ao fato da inclusão de
A
um parâmetro default como entrada na mesma.
Com as definições de rotas realizadas, cabe a realização do versionamento e merge:
it add .
g
git commit -m “Defining elements to API version”
git checkout master
git merge setting-api-base
esta forma já podemos trabalhar com a gem principal para auxílio na adição de usuários
D
adicionando-a no gemfile:
gem ‘devise’
Executar o comando:bundle
sses comandos foram suficientes para gerar uma migração para o usuário, criar um model
E
User, inserir configurações no arquivo de rotas e arquivos para testes.
s pec/factories/users.rb
FactoryBot.define do
factory :user do
email { Faker::Internet.email }
password {"123456"}
password_confirmation {"123456"}
end
end
sta fábrica quando instanciada com o devido método irá possibilitar criar o objeto para uma
E
classe pelo qual queira-se testar.
it add .
g
git commit -m "Adding devise user model"
Neste momento já estamos apto a realizar nosso primeiro teste para verificação do user:
require 'rails_helper'
it add .
g
git commit -m "Adding first tests to user model"
Pa
ra melhorar a velocidade dos testes utiliza-se umpreloader no grupo de desenvolvimento:
gem 'spring-commands-rspec'
config.order = :random
este contexto serão validado para um usuário User para que, quando o nome estiver em
N
branco, que ele não seja válido.
ara se complementar o teste entretanto, deve-se realizar uma validação no model user (com
P
um atributo virtual name):
ttr_accessor :name
a
validates_presence_of :name
E para que a elaboração do teste seja mais enxuto, elabora-se através do shoulda-matcher:
it add .
g
git commit -m “Adding shoulda-matcher at spec users ”
este momento o projeto já possui mais lastro para se iniciar com a criação do primeiro
N
endpoint. O primeiro endpoint estará relacionado a criação de um controller para o user, não se
esquecendo da nomenclatura utilizada para definição do versionamento da aplicação:
elativamente aos testes, os mesmo serão realizados utilizando o request test pois são mais
R
completos abrangendo testes de requisição e rotas.
lém de estar seguindo as boas práticas de que um teste de controller ser um teste de caráter
A
muito granular sendo considerado muitas vezes como antiprodutivo.
essa forma na pasta spec, definimos primeiramente a estrutura de onde os testes irão serão
D
organizados:
no arquivo de teste, uma diferença em relação ao teste do model user é que haverá a
E
persistência no banco para isso devemos estabelecer a sintaxe a seguir:
let!(:user){ create(:user) }
controllers/api/v1/users_controller.rb:
. . .
class Api::V1::UsersController < ApplicationController
def show
begin
@user = User.find(params[:id])
respond_with @user
rescue
head 404
end
end
nd
e
. . .
Nota: Lembrar que para que as urls devem estar nas whitelists da aplicação
config/environments/development.rb e development.rbcomconfig.hosts.clear
Versionando:
it add .
g
git commit -m “git commit -m "Adding action:show and tests:happy and sad path"
Podemos realizar um teste de conformidade no curl:
* Trying 127.0.0.1:3000...
* TCP_NODELAY set
* Connected to api.taskmanager.test (127.0.0.1) port 3000 (#0)
> GET /users/1 HTTP/1.1
> Host: api.taskmanager.test:3000
> User-Agent: curl/7.68.0
> Accept: application/vnd.taskmanager.v1
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< X-Download-Options: noopen
< X-Permitted-Cross-Domain-Policies: none
< Referrer-Policy: strict-origin-when-cross-origin
< Content-Type: application/json
< Cache-Control: no-cache
< X-Request-Id: 3f8e4ba1-688f-4b7a-959b-2413f4155d27
< X-Runtime: 0.025294
< Transfer-Encoding: chunked
<
* Connection #0 to host api.taskmanager.test left intact
Mas podemos usar o rails console para realizar um cadastro para testes
r ails c
user = User.create(email: “teste@teste.com”, password = “123456”)
E a resposta no curl já será:
* Trying 127.0.0.1:3000...
* TCP_NODELAY set
* Connected to api.taskmanager.test (127.0.0.1) port 3000 (#0)
> GET /users/1 HTTP/1.1
> Host: api.taskmanager.test:3000
> User-Agent: curl/7.68.0
> Accept: application/vnd.taskmanager.v1
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< X-Download-Options: noopen
< X-Permitted-Cross-Domain-Policies: none
< Referrer-Policy: strict-origin-when-cross-origin
< Content-Type: application/json; charset=utf-8
< ETag: W/"6bc939080a78a3fd3468c6eefac86c6e"
< Cache-Control: max-age=0, private, must-revalidate
< X-Request-Id: 428b2ac8-b37c-4f02-9522-8245c5c2d28a
< X-Runtime: 0.108343
< Transfer-Encoding: chunked
<
* Connection #0 to host api.taskmanager.test left intact
{ "id":1,"email":"teste@teste.com","created_at":"2020-08-11T17:56:30.562Z","updated_at
":"2020-08-11T17:56:30.562Z"}
ssim antes de mais nada implementa-se os testes nos contextos de parâmetros válidos e
A
inválidos:
end
end
end
end
Inicialmente estes testes irão falhar por não haver a rota, nem as ações implementadas, desta
forma e atendendo às condições solicitadas pelo teste obteremos:
routes.rb:
resources :users, only: [:show, :create, :update, :destroy]
ontrollers/api/v1/users_controller.rb:
c
. . .
def create
user = User.new(user_params)
if user.save
render json: user, status: 201
else
render json: { errors: user.errors}, status: 422
end
end
def update
user = User.find(params[:id])
if user.update(user_params)
render json: user, status: 200
else
render json: { errors: user.errors}, status: 422
end
end
def destroy
user = User.find(params[:id])
user.destroy
head 204
end
rivate
p
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
Refatorações:
Torna-se a variável:
let(:headers) do
{'Accept'=>'application/vnd.taskmanager.v1', 'Content-Type'=> Mime[:json].to_s}
end
Alem do mais deve-se converter o params que recebem corpo com o metodo to_json:
params:{user: user_params}.to_json
omo há muita repetição sempre na conversão de uma resposta para json, pode-se realizar a
C
seguinte alteração:
● A
dição da instrução para se carregar o método diretamente, exclusivo para os testes do
tipo request:
config.include RequestSpecHelper, type: :request
ode-se agora realizar a atualização do repositório e merge do mesmo, uma vez que essa
P
etapa foi concluída.
Terceira branch: Autenticação de usuários
Essa migração resultará na adição de uma nova coluna à tabela users denominada
auth_tokencom índice único.
lem do mais para que não haja nenhuma inconsistência entre o banco de desenvolvimento e
A
teste executar o comando:
rails db:test:prepare
ocks são utilizados para retornar algum resultado que não se possua controle, por exemplo o
M
caso dos tokens “cada hora que se solicita ao método um token, um token diferente é retornado
e isso implica que um teste sempre irá falhar”, dessa forma utilizamos informações mocks.
E como fazer: objeto ou método dublê (diz como o método deve se comportar):
allow(Devise).to receive(:friendly_token).and_return(‘abc123xyzTOKEN’)
Nota sobre significados da exclamação:
● u ser.save!→ tenta salvar o usuário e se não salvarretorna um resultado booleano false
e retorne uma exceção
● describe '#generation_authenticate_token!' →nestecaso irá alterar o estado do objeto
ara o caso de uma implementação utilizando recursos mocks podemos apresentar para o
P
método de geração de token de autenticação o seguinte trecho de código:
it 'generate a unique token' do
allow(Devise).to receive(:friendly_token).and_return('abc123xyzTOKEN')
user.generate_authentication_token!
expect(user.auth_token).to eq('abc123xyzTOKEN')
end
def generate_authentication_token!
self.auth_token = Devise.friendly_token
end
m outras palavras sempre que for executado o método para geração de um token no model é
E
solicitado que o dado seja mocado e substituído pelo que está sendo retornado, isso para o
caso do início de uma sessão.
á no caso que existir o token, sempre que solicitar um novo token ao sistema o sistema
J
deverá informar um segundo token e que para o dublê será um valor retornado como sendo um
segundo parâmetro.
it 'generate another auth_token when the current auth token already has been taken'
do
allow(Devise).to
receive(:friendly_token).and_return('TOKENabc123xyz','TOKENabc123xyz','abcXYZ123
456789')
existing_user = create(:user)
user.generate_authentication_token!
expect(user.auth_token).not_to eq(existing_user.auth_token)
end
que implica que no model essa verificação deverá ser realizada, inserindo um trecho de
O
código adicional no método de geração de token para autenticação:
def generate_authentication_token!
begin
self.auth_token = Devise.friendly_token
end while User.exists?(auth_token: auth_token)
end
Além do que este método deverá ser executado sempre que um usuário for criado:
before_create :generate_authentication_token!
essions:
S
Para que um usuário possua uma sessão é necessário criar um controller para a mesma de
forma que lhe seja permitido realizar ações numa área restrita.
esta forma há a necessidade antes de mais nada de elaborar o ciclo de testes para
D
verificação dos elementos na camada de controle estão implementados.
ara isso é necessário indicar o host onde se será realizado o teste, prepara as variaveis user
P
e header que serão utilizadas no momento do teste:
before do
post "/sessions", headers: headers, params: {session: credentials }.to_json
end
private
def session_params
params.require(:session).permit(:email,:password)
end
método sign_in é um helper do devise que tem como função realizar o inicio de uma sessão
O
de um usuário, mantendo registro no banco de dados que o usuário foi logado, o parametro
store: false garante que não haverá gravação de estado ipor se tratar de REST.
ambém as rotas devem ser ajustadas para definir que será aplicado apenas a controle de
T
sessões para as sessões definidas nesta api:
as deverá ser inserido que após a inserção do helper do devise que seja gerado um token e
M
que o usuário seja persistido no banco:
ser.generate_authetication_token!
u
user.save
nalogamente o teste deve ser atualizado porque o usuário deverá ser recarregado para que o
A
token seja o mesmo, uma vez que foi modificado.
user.reload
expect(json_body[:auth_token]).to eq(user.auth_token)
else
render json: {errors: 'Invalid password or email'}, status: 401
ara realizar um logout basata tratar a aplicação com a inserção de um teste para o metodo
P
delete do auth_token, seguido da inserçãpo da rota com o metodo destroy para session e
assim implementar a action no controller.
ponto de entrada dos controllers é o ApplicationController, uma vez que os demais controllers
O
herdam do mesmo, porém os métodos estarão na pasta concern para esta camada