SlideShare a Scribd company logo
1 of 54
RUBY
COMO DOMINAR SEU FLUXO DE
DESENVOLVIMENTO COM TDD
SOBRE
RACHID CALAZANS
▸Software Engineer at Tidy
▸Viciado em testes, fissurado em arquiteturas de software e
em qualidade de código
▸Github: /rachidcalazans
▸Medium: /@rachidcalazans
▸Podcast: http://2devs.simplecast.fm
▸Twitter: /rachidcalazans
DIFICULDADES
JÁ PASSOU POR ALGUMA?
▸Com Prazo?
▸Com solicitações mal escrita ou pouca informação?
▸Em saber como iniciar alguma Task?
▸Em aplicar testes na hora de implementar?
▸Em entregar um resultado meia boca ou insatisfatório?
SOLUÇÃO
O QUE PODEMOS FAZER?
▸Se organizar
▸Entender o que foi solicitado
▸Utilizar metodologia TDD (Desenvolvimento Orientado por Testes)
▸Entregar resultado final com qualidade
DOMINANDO FLUXO
OK, MAS COMO?
▸Utilizando uma metodologia de fluxo de desenvolvimento
▸Em apenas 3 Passos
▸Todo desenvolvedor deve realizar
▸Servirá sempre. Utilizando testes ou não
METODOLOGIA
OS 3 PASSOS
▸Passo 2 - Specs, Tickets, Tasks, Documentação
▸Passo 3 - Implementação
▸Passo 1 - Perguntas e Respostas
PASSO 1
PERGUNTAS CHAVES
▸1- Já estruturou o que será necessário fazer?
▸2- Qual camada(s) será necessário desenvolver?
▸Implementação de tela
▸Consumo da API
▸Criação de novo endpoint da API
▸3- Onde será preciso focar primeiro?
PASSO 1 - JÁ ESTRUTUROU O QUE SERÁ NECESSÁRIO FAZER?
RESPOSTA #1 - ESTRUTURAÇÃO
▸Sempre estruturar o que é necessário ANTES de botar a
mão no código.
▸Criar documentos auxiliares
▸Documentos ajudará a analisar o que foi solicitado
▸Documentos também ajudará a esclarecer se está tudo
dentro dos requerimentos
▸Ter um mapa de passo-a-passo do que irá realizar
PASSO 1 - QUAL CAMADA(S) SERÁ NECESSÁRIO DESENVOLVER?
RESPOSTA #2 - CAMADAS
▸Independente de qual for a camada, inicie priorizando pelo
topo (resultado final)
▸Realize um Fluxo de Desenvolvimento para cada Camada
PASSO 1 - ONDE SERÁ PRECISO FOCAR PRIMEIRO?
RESPOSTA #3 - FOCO
▸Na funcionalidade
▸Evite planejar toda a arquitetura que será utilizada
▸Foque no objetivo final
▸Foque na entrega que precisa ser realizada
PASSO 2
SPECS - TASKS - TICKETS
▸Extrair sempre o máximo as informações corretas
▸Montar seu DOCUMENTO auxiliar baseado nas
informações
▸Specs podem vir BEM estruturados ou NÃO
PASSO 2
EXEMPLO SPEC
▸Adicionar fotos para uma Inspeção
▸Cada foto pode ter uma descrição
PASSO 2
ESSA SPEC FOI BEM ESCRITA?
▸Não!
PASSO 2
EXTRAINDO INFORMAÇÕES
▸Uma Inspeção poderá ter muitas Fotos
▸Cada foto poderá ter uma descrição
▸Qual o formato do recebimento da foto (Url, Base64)?
▸Qual o formato de retorno da foto (Url, Base64)?
▸Como será o Endpoint?
▸Quais ações precisará ser feita?
▸Precisará criar apenas Foto?
▸Precisará enviar algum email ou confirmação?
▸Será recebido múltiplas fotos no endpoint?
PASSO 2
CRIAR DOCUMENTAÇÃO AUXILIAR
▸Vamos utilizar o Mapa Mental para documentar
▸Vamos Mapear:
▸Passo-a-passo
▸O que iremos utilizar
▸Estrutura
▸Adicionar nele toda a extração feita
PASSO 2
MAPA MENTAL
▸Local Link
▸Mostrar exemplo real também
PASSO 3
IMPLEMENTAÇÃO
▸Focar o resultado final
▸Utilizar TDD
▸Iniciar pelo Topo
▸Iniciar sem pensar muito a frente na Estrutura
▸Fazer em Baby steps (Passos de Bebê)
PASSO 3
NOVO PROJETO
$ rails new inspection_app --api
PASSO 3
group :development, :test do
gem 'pry'
end
group :test do
gem 'rspec-rails'
end
* GEMFILE
▸Atualizar o arquivo Gemfile
PASSO 3
$ rails generate rspec:install
* .rspec
* spec/spec_helper.rb
* spec/rails_helper.rb
▸Incializar a Gem rspec
PASSO 3
require 'rails_helper'
describe 'Api::V1::Inspections::PhotosController' do
it 'It is working' do
expect(true).to be true
end
end
▸Criar primeiro o arquivo de teste para verificar se está tudo funcionando
* spec/controllers/api/v1/inspections/photos_controller_spec.rb
PASSO 3
▸Teste passando
$ rspec --fd
$ rspec
PASSO 3
require 'rails_helper'
describe Api::V1::Inspections::PhotosController do
it 'It is working' do
expect(true).to be true
end
end
▸Atualizar o teste
* spec/controllers/api/v1/inspections/photos_controller_spec.rb
PASSO 3
▸Teste falhando
$ rspec
PASSO 3
module Api
module V1
module Inspections
class PhotosController < ApplicationController
end
end
end
end
▸Criar PhotosController
* app/controllers/api/v1/inspections/photos_controller.rb
PASSO 3
▸Teste passando
$ rspec
PASSO 3
require 'rails_helper'
describe Api::V1::Inspections::PhotosController, type: :controller do
describe 'POST /api/v1/inspections/:inspection_id/photos' do
before do
post :create, params: { inspection_id: 1 }
end
it 'Should create an inspection photo' do
expect(response.status).to be == 201
end
end
end
▸Adicionar primeiro endpoint
* spec/controllers/api/v1/inspections/photos_controller_spec.rb
PASSO 3
▸Teste falhando
$ rspec
PASSO 3
module Api
module V1
module Inspections
class PhotosController < ApplicationController
def create
render json: {}, status: 201
end
end
end
end
end
▸Atualizar Controller - Adicionar o create método
* app/controllers/api/v1/inspections/photos_controller.rb
PASSO 3
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
namespace :inspections do
post ':inspection_id/photos', to: 'photos#create'
end
end
end
end
▸Editar Rotas
* config/routes.rb
PASSO 3
▸Teste passando
$ rspec
PASSO 3
describe 'POST /api/v1/inspections/:inspection_id/photos' do
let(:json) { JSON.parse response.body }
before do
post :create, params: { inspection_id: 1 }
end
it 'Should create an inspection photo' do
expect(response.status).to be == 201
expected_response = {
'id' => 11,
'inspection_id' => 1,
'photo_url' => 'http://chid.com/inspection/photo_some_hash_64',
'caption' => nil,
}
expect(json).to match expected_response
end
end
▸Atualizar o Expect result no teste
* spec/controllers/api/v1/inspections/photos_controller_spec.rb
PASSO 3
▸Teste falhando
$ rspec
PASSO 3
def create
Struct.new('InspectionPhotoPresenter',
:id, :inspection_id, :photo_url, :caption, keyword_init: true)
presenter_params = {
id: 11,
inspection_id: 1,
photo_url: 'http://chid.com/inspection/photo_some_hash_64',
caption: nil,
}
presenter = Struct::InspectionPhotoPresenter.new(presenter_params)
render json: presenter, status: 201
end
▸Atualizar Controller - Editar método create
* app/controllers/api/v1/inspections/photos_controller.rb
PASSO 3
▸Teste passando
$ rspec
PASSO 3
describe 'POST /api/v1/inspections/:inspection_id/photos' do
let(:json) { JSON.parse response.body }
before do
post :create, params: { inspection_id: 1, photo_base_64: 'some_hash_64' }
end
it 'Should create an inspection photo' do
expect(response.status).to be == 201
expected_response = {
'id' => 11,
'inspection_id' => 1,
'photo_url' => 'http://chid.com/inspection/photo_some_hash_64',
'caption' => nil,
}
expect(json).to match expected_response
end
end
▸Atualizar parâmetros de request
* spec/controllers/api/v1/inspections/photos_controller_spec.rb
PASSO 3
▸Teste passando
$ rspec
PASSO 3
def create
Struct.new('InspectionPhotoPresenter', :id, :inspection_id, :photo_url, :caption, keyword_init: true)
presenter_params = photo_params
.merge(id: 11, photo_url: url_from_base_64(photo_params[:photo_base_64]))
.except(:photo_base_64)
.to_h
presenter = Struct::InspectionPhotoPresenter.new(presenter_params)
render json: presenter, status: 201
end
private
def url_from_base_64(base_64)
"http://chid.com/inspection/photo_#{base_64}"
end
def photo_params
permitted_keys = %i[inspection_id photo_base_64 caption]
params.permit(permitted_keys)
end
▸Atualizar método create - Utilizar request params
* app/controllers/api/v1/inspections/photos_controller.rb
PASSO 3
▸Teste passando
$ rspec
PASSO 3
class CreateInspection < ActiveRecord::Migration[5.2]
def change
create_table :inspections do |t|
t.string :description
t.timestamps null: false
end
end
end
▸Adicionar Banco de Dados - Inspection Migration
* db/migrate/20180922035522_create_inspection.rb
PASSO 3
class CreateInspectionPhoto < ActiveRecord::Migration[5.2]
def change
create_table :inspection_photos do |t|
t.references :inspection, foreign_key: true, null: false
t.string :photo_url, null: false
t.string :caption
t.timestamps null: false
end
end
end
▸InspectionPhoto Migration
* db/migrate/20180922035631_create_inspection_photo.rb
PASSO 3
$ RAILS_ENV=test rake db:migrate
▸Run migrations
PASSO 3
▸Teste passando
$ rspec
PASSO 3
describe 'POST /api/v1/inspections/:inspection_id/photos' do
let(:json) { JSON.parse response.body }
let(:inspection) { ::Inspection.create(description: 'Inspection 001') }
before do
post :create, params: { inspection_id: inspection.id, photo_base_64: 'some_hash_64' }
end
it 'Should create an inspection photo' do
expect(response.status).to be == 201
expected_response = {
'id' => 11,
'inspection_id' => inspection.id,
'photo_url' => 'http://chid.com/inspection/photo_some_hash_64',
'caption' => nil,
}
expect(json).to match expected_response
end
end
▸Atualizar Setup para utilizar Banco de Dados
* spec/controllers/api/v1/inspections/photos_controller_spec.rb
PASSO 3
▸Teste falhando
$ rspec
PASSO 3
class Inspection < ApplicationRecord
end
▸Criar Model - Inspection
* app/models/inspection.rb
PASSO 3
▸Criar Model - InspectionPhoto
* app/models/inspection_photo.rb
class InspectionPhoto < ApplicationRecord
belongs_to :inspection
end
PASSO 3
▸Teste falhando
$ rspec
PASSO 3
▸Utilizar ActiveRecord para registrar no Banco
* app/controllers/api/v1/inspections/photos_controller.rb
def create
Struct.new('InspectionPhotoPresenter', :id, :inspection_id, :photo_url,
:caption, keyword_init: true)
request_params = photo_params
.merge(photo_url: url_from_base_64(photo_params[:photo_base_64]))
.except(:photo_base_64)
.to_h
inspection_photo = ::InspectionPhoto.create(request_params)
presenter_params = inspection_photo
.attributes
.except('created_at', 'updated_at')
presenter = Struct::InspectionPhotoPresenter.new(presenter_params)
render json: presenter, status: 201
end
PASSO 3
▸Teste passando
$ rspec
PASSO 3
O QUE TEMOS ATÉ AGORA?
▸Cobertura do código com os testes de Integração
▸Resultado final de acordo com o que foi solicitado
PASSO 3
IMPLEMENTAÇÃO ACABOU?
▸Agora que é a hora de:
▸ Começar a Refatorar para valer!
▸Arquiteturar o necessário para seu projeto
▸Nada!
▸Adicionar UseCases, Services, Presenters, Repositories etc
PASSO 3
IMPLEMENTAÇÃO - RESULTADO FINAL
▸Github repositório:
https://github.com/rachidcalazans/inspection_app_final.git
OBRIGADO
!

More Related Content

What's hot

Introdução à MEAN Stack
Introdução à MEAN StackIntrodução à MEAN Stack
Introdução à MEAN StackBruno Catão
 
Testando Rails apps com RSpec
Testando Rails apps com RSpecTestando Rails apps com RSpec
Testando Rails apps com RSpecNando Vieira
 
Maven 3, Sonar e Hudson
Maven 3, Sonar e HudsonMaven 3, Sonar e Hudson
Maven 3, Sonar e HudsonRodrigo Branas
 
Código legado - PHP Conference Brasil - 2014
Código legado - PHP Conference Brasil - 2014Código legado - PHP Conference Brasil - 2014
Código legado - PHP Conference Brasil - 2014Michael Castillo Granados
 
Integração Contínua com CruiseControl e phpUnderControl
Integração Contínua com CruiseControl e phpUnderControlIntegração Contínua com CruiseControl e phpUnderControl
Integração Contínua com CruiseControl e phpUnderControlDiego Tremper
 
Integracao Contínua com CruiseControl e phpUnderControl
Integracao Contínua com CruiseControl e phpUnderControlIntegracao Contínua com CruiseControl e phpUnderControl
Integracao Contínua com CruiseControl e phpUnderControlDiego Tremper
 
Os 10 maus habitos dos desenvolvedores jsf (JustJava e CCT)
Os 10 maus habitos dos desenvolvedores jsf (JustJava e CCT)Os 10 maus habitos dos desenvolvedores jsf (JustJava e CCT)
Os 10 maus habitos dos desenvolvedores jsf (JustJava e CCT)Rafael Ponte
 
Introdução a Hooks - Aprenda a customizar o WordPress com filtros e ações
Introdução a Hooks - Aprenda a customizar o WordPress com filtros e açõesIntrodução a Hooks - Aprenda a customizar o WordPress com filtros e ações
Introdução a Hooks - Aprenda a customizar o WordPress com filtros e açõesfrq
 
Boas práticas de django
Boas práticas de djangoBoas práticas de django
Boas práticas de djangoFilipe Ximenes
 
Offline Web com Service Workers - Sérgio Lopes
Offline Web com Service Workers - Sérgio LopesOffline Web com Service Workers - Sérgio Lopes
Offline Web com Service Workers - Sérgio LopesCaelum
 

What's hot (11)

Introdução à MEAN Stack
Introdução à MEAN StackIntrodução à MEAN Stack
Introdução à MEAN Stack
 
Testando Rails apps com RSpec
Testando Rails apps com RSpecTestando Rails apps com RSpec
Testando Rails apps com RSpec
 
Maven 3, Sonar e Hudson
Maven 3, Sonar e HudsonMaven 3, Sonar e Hudson
Maven 3, Sonar e Hudson
 
Spa com angular js flisol 2015 - aquidauana ms
Spa com angular js   flisol 2015 - aquidauana msSpa com angular js   flisol 2015 - aquidauana ms
Spa com angular js flisol 2015 - aquidauana ms
 
Código legado - PHP Conference Brasil - 2014
Código legado - PHP Conference Brasil - 2014Código legado - PHP Conference Brasil - 2014
Código legado - PHP Conference Brasil - 2014
 
Integração Contínua com CruiseControl e phpUnderControl
Integração Contínua com CruiseControl e phpUnderControlIntegração Contínua com CruiseControl e phpUnderControl
Integração Contínua com CruiseControl e phpUnderControl
 
Integracao Contínua com CruiseControl e phpUnderControl
Integracao Contínua com CruiseControl e phpUnderControlIntegracao Contínua com CruiseControl e phpUnderControl
Integracao Contínua com CruiseControl e phpUnderControl
 
Os 10 maus habitos dos desenvolvedores jsf (JustJava e CCT)
Os 10 maus habitos dos desenvolvedores jsf (JustJava e CCT)Os 10 maus habitos dos desenvolvedores jsf (JustJava e CCT)
Os 10 maus habitos dos desenvolvedores jsf (JustJava e CCT)
 
Introdução a Hooks - Aprenda a customizar o WordPress com filtros e ações
Introdução a Hooks - Aprenda a customizar o WordPress com filtros e açõesIntrodução a Hooks - Aprenda a customizar o WordPress com filtros e ações
Introdução a Hooks - Aprenda a customizar o WordPress com filtros e ações
 
Boas práticas de django
Boas práticas de djangoBoas práticas de django
Boas práticas de django
 
Offline Web com Service Workers - Sérgio Lopes
Offline Web com Service Workers - Sérgio LopesOffline Web com Service Workers - Sérgio Lopes
Offline Web com Service Workers - Sérgio Lopes
 

Similar to Como dominar seu fluxo de desenvolvimento com TDD em Ruby

Qualidade no desenvolvimento de software com PHPUnit
Qualidade no desenvolvimento de software com PHPUnitQualidade no desenvolvimento de software com PHPUnit
Qualidade no desenvolvimento de software com PHPUnitDiego Tremper
 
Desenvolvimento para a Web com CakePHP
Desenvolvimento para a Web com CakePHPDesenvolvimento para a Web com CakePHP
Desenvolvimento para a Web com CakePHPMarcelo Andrade
 
VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...
VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...
VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...Dextra Sistemas / Etec Itu
 
[TDC2016] Ruby in Tests: Automatizando testes de Unidade, API e GUI (Web)
[TDC2016] Ruby in Tests: Automatizando testes de Unidade, API e GUI (Web)[TDC2016] Ruby in Tests: Automatizando testes de Unidade, API e GUI (Web)
[TDC2016] Ruby in Tests: Automatizando testes de Unidade, API e GUI (Web)Júlio de Lima
 
Tutorial visão automação de testes e casper js
Tutorial visão automação de testes e casper jsTutorial visão automação de testes e casper js
Tutorial visão automação de testes e casper js4ALL Tests
 
Tutorial - Visão sobre Automação de Testes com CasperJS
Tutorial - Visão sobre Automação de Testes com CasperJSTutorial - Visão sobre Automação de Testes com CasperJS
Tutorial - Visão sobre Automação de Testes com CasperJSFrederico Allan
 
Angular 2 em 60 minutos
Angular 2 em 60 minutosAngular 2 em 60 minutos
Angular 2 em 60 minutosLoiane Groner
 
Vale Security Conference - 2011 - 6 - Thiago Bordini
Vale Security Conference - 2011 - 6 - Thiago BordiniVale Security Conference - 2011 - 6 - Thiago Bordini
Vale Security Conference - 2011 - 6 - Thiago BordiniVale Security Conference
 
Devfest Cerrado: Angular 2
Devfest Cerrado: Angular 2 Devfest Cerrado: Angular 2
Devfest Cerrado: Angular 2 Loiane Groner
 
Curso: Desenvolvimento de aplicativos híbridos (dia 2)
Curso: Desenvolvimento de aplicativos híbridos (dia 2)Curso: Desenvolvimento de aplicativos híbridos (dia 2)
Curso: Desenvolvimento de aplicativos híbridos (dia 2)Wennder Santos
 
ZF2 básico : Desenvolvendo um Blog com o Zend Framework 2
ZF2 básico : Desenvolvendo um Blog com o Zend Framework 2ZF2 básico : Desenvolvendo um Blog com o Zend Framework 2
ZF2 básico : Desenvolvendo um Blog com o Zend Framework 2Cezar Souza
 
Integração Contínua com CruiseControl e phpUnderControl
Integração Contínua com CruiseControl e phpUnderControlIntegração Contínua com CruiseControl e phpUnderControl
Integração Contínua com CruiseControl e phpUnderControlManuel Lemos
 
Qualidade no desenvolvimento de software com PHPUnit
Qualidade no desenvolvimento de software com PHPUnitQualidade no desenvolvimento de software com PHPUnit
Qualidade no desenvolvimento de software com PHPUnitDiego Tremper
 
Todas as abordagens de testes dentro do ágil
Todas as abordagens de testes dentro do ágilTodas as abordagens de testes dentro do ágil
Todas as abordagens de testes dentro do ágilElias Nogueira
 
Técnicas para preparação e desenvolvimento de sites em django
Técnicas para preparação e desenvolvimento de sites em djangoTécnicas para preparação e desenvolvimento de sites em django
Técnicas para preparação e desenvolvimento de sites em djangoMario Chaves
 
PHPUnit e teste de software
PHPUnit e teste de softwarePHPUnit e teste de software
PHPUnit e teste de softwarericardophp
 

Similar to Como dominar seu fluxo de desenvolvimento com TDD em Ruby (20)

Qualidade no desenvolvimento de software com PHPUnit
Qualidade no desenvolvimento de software com PHPUnitQualidade no desenvolvimento de software com PHPUnit
Qualidade no desenvolvimento de software com PHPUnit
 
Desenvolvimento para a Web com CakePHP
Desenvolvimento para a Web com CakePHPDesenvolvimento para a Web com CakePHP
Desenvolvimento para a Web com CakePHP
 
VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...
VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...
VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...
 
[TDC2016] Ruby in Tests: Automatizando testes de Unidade, API e GUI (Web)
[TDC2016] Ruby in Tests: Automatizando testes de Unidade, API e GUI (Web)[TDC2016] Ruby in Tests: Automatizando testes de Unidade, API e GUI (Web)
[TDC2016] Ruby in Tests: Automatizando testes de Unidade, API e GUI (Web)
 
Tutorial visão automação de testes e casper js
Tutorial visão automação de testes e casper jsTutorial visão automação de testes e casper js
Tutorial visão automação de testes e casper js
 
Tutorial - Visão sobre Automação de Testes com CasperJS
Tutorial - Visão sobre Automação de Testes com CasperJSTutorial - Visão sobre Automação de Testes com CasperJS
Tutorial - Visão sobre Automação de Testes com CasperJS
 
Angular 2 em 60 minutos
Angular 2 em 60 minutosAngular 2 em 60 minutos
Angular 2 em 60 minutos
 
Vale Security Conference - 2011 - 6 - Thiago Bordini
Vale Security Conference - 2011 - 6 - Thiago BordiniVale Security Conference - 2011 - 6 - Thiago Bordini
Vale Security Conference - 2011 - 6 - Thiago Bordini
 
Devfest Cerrado: Angular 2
Devfest Cerrado: Angular 2 Devfest Cerrado: Angular 2
Devfest Cerrado: Angular 2
 
Google apps script - Parte - 1
Google apps script - Parte - 1Google apps script - Parte - 1
Google apps script - Parte - 1
 
Curso: Desenvolvimento de aplicativos híbridos (dia 2)
Curso: Desenvolvimento de aplicativos híbridos (dia 2)Curso: Desenvolvimento de aplicativos híbridos (dia 2)
Curso: Desenvolvimento de aplicativos híbridos (dia 2)
 
Desafio Rest API
Desafio Rest APIDesafio Rest API
Desafio Rest API
 
ZF2 básico : Desenvolvendo um Blog com o Zend Framework 2
ZF2 básico : Desenvolvendo um Blog com o Zend Framework 2ZF2 básico : Desenvolvendo um Blog com o Zend Framework 2
ZF2 básico : Desenvolvendo um Blog com o Zend Framework 2
 
Integração Contínua com CruiseControl e phpUnderControl
Integração Contínua com CruiseControl e phpUnderControlIntegração Contínua com CruiseControl e phpUnderControl
Integração Contínua com CruiseControl e phpUnderControl
 
Qualidade no desenvolvimento de software com PHPUnit
Qualidade no desenvolvimento de software com PHPUnitQualidade no desenvolvimento de software com PHPUnit
Qualidade no desenvolvimento de software com PHPUnit
 
Php 05 Mvc
Php 05 MvcPhp 05 Mvc
Php 05 Mvc
 
Design Patterns em Ruby
Design Patterns em RubyDesign Patterns em Ruby
Design Patterns em Ruby
 
Todas as abordagens de testes dentro do ágil
Todas as abordagens de testes dentro do ágilTodas as abordagens de testes dentro do ágil
Todas as abordagens de testes dentro do ágil
 
Técnicas para preparação e desenvolvimento de sites em django
Técnicas para preparação e desenvolvimento de sites em djangoTécnicas para preparação e desenvolvimento de sites em django
Técnicas para preparação e desenvolvimento de sites em django
 
PHPUnit e teste de software
PHPUnit e teste de softwarePHPUnit e teste de software
PHPUnit e teste de software
 

Como dominar seu fluxo de desenvolvimento com TDD em Ruby

  • 1. RUBY COMO DOMINAR SEU FLUXO DE DESENVOLVIMENTO COM TDD
  • 2. SOBRE RACHID CALAZANS ▸Software Engineer at Tidy ▸Viciado em testes, fissurado em arquiteturas de software e em qualidade de código ▸Github: /rachidcalazans ▸Medium: /@rachidcalazans ▸Podcast: http://2devs.simplecast.fm ▸Twitter: /rachidcalazans
  • 3. DIFICULDADES JÁ PASSOU POR ALGUMA? ▸Com Prazo? ▸Com solicitações mal escrita ou pouca informação? ▸Em saber como iniciar alguma Task? ▸Em aplicar testes na hora de implementar? ▸Em entregar um resultado meia boca ou insatisfatório?
  • 4. SOLUÇÃO O QUE PODEMOS FAZER? ▸Se organizar ▸Entender o que foi solicitado ▸Utilizar metodologia TDD (Desenvolvimento Orientado por Testes) ▸Entregar resultado final com qualidade
  • 5. DOMINANDO FLUXO OK, MAS COMO? ▸Utilizando uma metodologia de fluxo de desenvolvimento ▸Em apenas 3 Passos ▸Todo desenvolvedor deve realizar ▸Servirá sempre. Utilizando testes ou não
  • 6. METODOLOGIA OS 3 PASSOS ▸Passo 2 - Specs, Tickets, Tasks, Documentação ▸Passo 3 - Implementação ▸Passo 1 - Perguntas e Respostas
  • 7. PASSO 1 PERGUNTAS CHAVES ▸1- Já estruturou o que será necessário fazer? ▸2- Qual camada(s) será necessário desenvolver? ▸Implementação de tela ▸Consumo da API ▸Criação de novo endpoint da API ▸3- Onde será preciso focar primeiro?
  • 8. PASSO 1 - JÁ ESTRUTUROU O QUE SERÁ NECESSÁRIO FAZER? RESPOSTA #1 - ESTRUTURAÇÃO ▸Sempre estruturar o que é necessário ANTES de botar a mão no código. ▸Criar documentos auxiliares ▸Documentos ajudará a analisar o que foi solicitado ▸Documentos também ajudará a esclarecer se está tudo dentro dos requerimentos ▸Ter um mapa de passo-a-passo do que irá realizar
  • 9. PASSO 1 - QUAL CAMADA(S) SERÁ NECESSÁRIO DESENVOLVER? RESPOSTA #2 - CAMADAS ▸Independente de qual for a camada, inicie priorizando pelo topo (resultado final) ▸Realize um Fluxo de Desenvolvimento para cada Camada
  • 10. PASSO 1 - ONDE SERÁ PRECISO FOCAR PRIMEIRO? RESPOSTA #3 - FOCO ▸Na funcionalidade ▸Evite planejar toda a arquitetura que será utilizada ▸Foque no objetivo final ▸Foque na entrega que precisa ser realizada
  • 11. PASSO 2 SPECS - TASKS - TICKETS ▸Extrair sempre o máximo as informações corretas ▸Montar seu DOCUMENTO auxiliar baseado nas informações ▸Specs podem vir BEM estruturados ou NÃO
  • 12. PASSO 2 EXEMPLO SPEC ▸Adicionar fotos para uma Inspeção ▸Cada foto pode ter uma descrição
  • 13. PASSO 2 ESSA SPEC FOI BEM ESCRITA? ▸Não!
  • 14. PASSO 2 EXTRAINDO INFORMAÇÕES ▸Uma Inspeção poderá ter muitas Fotos ▸Cada foto poderá ter uma descrição ▸Qual o formato do recebimento da foto (Url, Base64)? ▸Qual o formato de retorno da foto (Url, Base64)? ▸Como será o Endpoint? ▸Quais ações precisará ser feita? ▸Precisará criar apenas Foto? ▸Precisará enviar algum email ou confirmação? ▸Será recebido múltiplas fotos no endpoint?
  • 15. PASSO 2 CRIAR DOCUMENTAÇÃO AUXILIAR ▸Vamos utilizar o Mapa Mental para documentar ▸Vamos Mapear: ▸Passo-a-passo ▸O que iremos utilizar ▸Estrutura ▸Adicionar nele toda a extração feita
  • 16. PASSO 2 MAPA MENTAL ▸Local Link ▸Mostrar exemplo real também
  • 17. PASSO 3 IMPLEMENTAÇÃO ▸Focar o resultado final ▸Utilizar TDD ▸Iniciar pelo Topo ▸Iniciar sem pensar muito a frente na Estrutura ▸Fazer em Baby steps (Passos de Bebê)
  • 18. PASSO 3 NOVO PROJETO $ rails new inspection_app --api
  • 19. PASSO 3 group :development, :test do gem 'pry' end group :test do gem 'rspec-rails' end * GEMFILE ▸Atualizar o arquivo Gemfile
  • 20. PASSO 3 $ rails generate rspec:install * .rspec * spec/spec_helper.rb * spec/rails_helper.rb ▸Incializar a Gem rspec
  • 21. PASSO 3 require 'rails_helper' describe 'Api::V1::Inspections::PhotosController' do it 'It is working' do expect(true).to be true end end ▸Criar primeiro o arquivo de teste para verificar se está tudo funcionando * spec/controllers/api/v1/inspections/photos_controller_spec.rb
  • 22. PASSO 3 ▸Teste passando $ rspec --fd $ rspec
  • 23. PASSO 3 require 'rails_helper' describe Api::V1::Inspections::PhotosController do it 'It is working' do expect(true).to be true end end ▸Atualizar o teste * spec/controllers/api/v1/inspections/photos_controller_spec.rb
  • 25. PASSO 3 module Api module V1 module Inspections class PhotosController < ApplicationController end end end end ▸Criar PhotosController * app/controllers/api/v1/inspections/photos_controller.rb
  • 27. PASSO 3 require 'rails_helper' describe Api::V1::Inspections::PhotosController, type: :controller do describe 'POST /api/v1/inspections/:inspection_id/photos' do before do post :create, params: { inspection_id: 1 } end it 'Should create an inspection photo' do expect(response.status).to be == 201 end end end ▸Adicionar primeiro endpoint * spec/controllers/api/v1/inspections/photos_controller_spec.rb
  • 29. PASSO 3 module Api module V1 module Inspections class PhotosController < ApplicationController def create render json: {}, status: 201 end end end end end ▸Atualizar Controller - Adicionar o create método * app/controllers/api/v1/inspections/photos_controller.rb
  • 30. PASSO 3 Rails.application.routes.draw do namespace :api do namespace :v1 do namespace :inspections do post ':inspection_id/photos', to: 'photos#create' end end end end ▸Editar Rotas * config/routes.rb
  • 32. PASSO 3 describe 'POST /api/v1/inspections/:inspection_id/photos' do let(:json) { JSON.parse response.body } before do post :create, params: { inspection_id: 1 } end it 'Should create an inspection photo' do expect(response.status).to be == 201 expected_response = { 'id' => 11, 'inspection_id' => 1, 'photo_url' => 'http://chid.com/inspection/photo_some_hash_64', 'caption' => nil, } expect(json).to match expected_response end end ▸Atualizar o Expect result no teste * spec/controllers/api/v1/inspections/photos_controller_spec.rb
  • 34. PASSO 3 def create Struct.new('InspectionPhotoPresenter', :id, :inspection_id, :photo_url, :caption, keyword_init: true) presenter_params = { id: 11, inspection_id: 1, photo_url: 'http://chid.com/inspection/photo_some_hash_64', caption: nil, } presenter = Struct::InspectionPhotoPresenter.new(presenter_params) render json: presenter, status: 201 end ▸Atualizar Controller - Editar método create * app/controllers/api/v1/inspections/photos_controller.rb
  • 36. PASSO 3 describe 'POST /api/v1/inspections/:inspection_id/photos' do let(:json) { JSON.parse response.body } before do post :create, params: { inspection_id: 1, photo_base_64: 'some_hash_64' } end it 'Should create an inspection photo' do expect(response.status).to be == 201 expected_response = { 'id' => 11, 'inspection_id' => 1, 'photo_url' => 'http://chid.com/inspection/photo_some_hash_64', 'caption' => nil, } expect(json).to match expected_response end end ▸Atualizar parâmetros de request * spec/controllers/api/v1/inspections/photos_controller_spec.rb
  • 38. PASSO 3 def create Struct.new('InspectionPhotoPresenter', :id, :inspection_id, :photo_url, :caption, keyword_init: true) presenter_params = photo_params .merge(id: 11, photo_url: url_from_base_64(photo_params[:photo_base_64])) .except(:photo_base_64) .to_h presenter = Struct::InspectionPhotoPresenter.new(presenter_params) render json: presenter, status: 201 end private def url_from_base_64(base_64) "http://chid.com/inspection/photo_#{base_64}" end def photo_params permitted_keys = %i[inspection_id photo_base_64 caption] params.permit(permitted_keys) end ▸Atualizar método create - Utilizar request params * app/controllers/api/v1/inspections/photos_controller.rb
  • 40. PASSO 3 class CreateInspection < ActiveRecord::Migration[5.2] def change create_table :inspections do |t| t.string :description t.timestamps null: false end end end ▸Adicionar Banco de Dados - Inspection Migration * db/migrate/20180922035522_create_inspection.rb
  • 41. PASSO 3 class CreateInspectionPhoto < ActiveRecord::Migration[5.2] def change create_table :inspection_photos do |t| t.references :inspection, foreign_key: true, null: false t.string :photo_url, null: false t.string :caption t.timestamps null: false end end end ▸InspectionPhoto Migration * db/migrate/20180922035631_create_inspection_photo.rb
  • 42. PASSO 3 $ RAILS_ENV=test rake db:migrate ▸Run migrations
  • 44. PASSO 3 describe 'POST /api/v1/inspections/:inspection_id/photos' do let(:json) { JSON.parse response.body } let(:inspection) { ::Inspection.create(description: 'Inspection 001') } before do post :create, params: { inspection_id: inspection.id, photo_base_64: 'some_hash_64' } end it 'Should create an inspection photo' do expect(response.status).to be == 201 expected_response = { 'id' => 11, 'inspection_id' => inspection.id, 'photo_url' => 'http://chid.com/inspection/photo_some_hash_64', 'caption' => nil, } expect(json).to match expected_response end end ▸Atualizar Setup para utilizar Banco de Dados * spec/controllers/api/v1/inspections/photos_controller_spec.rb
  • 46. PASSO 3 class Inspection < ApplicationRecord end ▸Criar Model - Inspection * app/models/inspection.rb
  • 47. PASSO 3 ▸Criar Model - InspectionPhoto * app/models/inspection_photo.rb class InspectionPhoto < ApplicationRecord belongs_to :inspection end
  • 49. PASSO 3 ▸Utilizar ActiveRecord para registrar no Banco * app/controllers/api/v1/inspections/photos_controller.rb def create Struct.new('InspectionPhotoPresenter', :id, :inspection_id, :photo_url, :caption, keyword_init: true) request_params = photo_params .merge(photo_url: url_from_base_64(photo_params[:photo_base_64])) .except(:photo_base_64) .to_h inspection_photo = ::InspectionPhoto.create(request_params) presenter_params = inspection_photo .attributes .except('created_at', 'updated_at') presenter = Struct::InspectionPhotoPresenter.new(presenter_params) render json: presenter, status: 201 end
  • 51. PASSO 3 O QUE TEMOS ATÉ AGORA? ▸Cobertura do código com os testes de Integração ▸Resultado final de acordo com o que foi solicitado
  • 52. PASSO 3 IMPLEMENTAÇÃO ACABOU? ▸Agora que é a hora de: ▸ Começar a Refatorar para valer! ▸Arquiteturar o necessário para seu projeto ▸Nada! ▸Adicionar UseCases, Services, Presenters, Repositories etc
  • 53. PASSO 3 IMPLEMENTAÇÃO - RESULTADO FINAL ▸Github repositório: https://github.com/rachidcalazans/inspection_app_final.git