Skip to content

Instantly share code, notes, and snippets.

@kurko
Created November 8, 2011 05:04
Show Gist options
  • Save kurko/1347050 to your computer and use it in GitHub Desktop.
Save kurko/1347050 to your computer and use it in GitHub Desktop.
SOLID e BDD
# No ActiveRecord, Client has_one ClientProfile
#
# ClientProfile tem um campo chamado Twitter. Como é um varchar
# e um usuário pode ter 2 contas no Twitter, salvo os dados no
# formato "usuario;usuario2;usuario3" neste campo. O problema abaixo gira em torno
# do processamento desta string e como testar isto usando os princípios SOLID que
# você abordou na sua palestra na RubyConf.
#
# O split(";") era feito no model, o qual tirei para fora (classe ClientService abaixo).
# O problema é: como vou testar com mock se o argumento injetado precisa ser ActiveRecord?
# Afinal, vou fazer um loop por Client.client_profile.twitter.split(";").each { ... }
# Questão 1: Vê Client.client_profile.twitter.split() no código? Se Client é enviado
# a organize_twitter() como argumento no controller abaixo, como testar com mock?
# Pois se usar um mock como argumento, vai quebrar tudo. Me parece que há algo
# estranho no design, pois como você disse, "Teste quebradiço = Design ruim"...
# Questão 2: Eu testava models com dados vindos do DB. Usando Mock, vou testar
# o controller (integração) e os Services, mas e os models? Não testarei mais usando Factory
# e afins? Ou seja, a única forma de testar resultado real do comportamento da app será por
# testes de controller? (já que os services recebem injeção de mocks apenas)
# Questão 3: O método organize_twitter() deveria sair de ClientService e ir para outra classe?
# Como você interpreta a responsabilidade dele?
# app/controller/clients_controller.rb
def show
client = ClientService.new(session[:company_id])
@client = client.find(Client, params[:id])
@twitter = client.organize_twitter(@client)
end
# app/services/clients_service.rb
#
# Todo este método estava num model. Então tirei ele para fora.
class ClientService
def initialize(company_id)
@company_id = company_id
end
def find(client_active_record, id)
client_active_record.by_company(@company_id).find(id)
end
# Relacionado à Questão 1: Como testarei este método, se o argumento é um ActiveRecord?
# Entraria aqui um Wrapper, que faz esse loop todo e então injeta uma array em
# vez de um objeto ActiveRecord? Como usar mocks?
def organize_twitter(client)
twitter = []
client.client_profile.twitter.to_s.gsub(/\s/, '').split(/[\s|,|;]/).each { |t|
twitter << { :role => 'client', :username => t }
}
# Este loop é porque Client has_many Contacts, os quais também têm Twitter.
client.contacts.each { |contact|
contact.client_profile.twitter.gsub(/\s/, '').split(/[\s|,|;]/).each { |t|
twitter << { :role => 'contact', :username => t }
}
}
twitter
end
end
# Meu teste até agora para isto
# spec/services/client_spec.rb
require './app/services/client_service'
describe ClientService do
let(:client) { double("a client") }
it "loads" do
client.stub_chain(:by_company, :find).and_return true
ClientService.new(1).find(client, 1).should be_true
end
it "loads twitter accounts" do
# ...aqui eu to penando...
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment