Magnun Leno : Curso de Filosofia GNU - Parte 8
22 de Abril de 2014, 21:40 - sem comentários aindaNo penúltimo texto do Curso de Filosofia GNU "Linux e o Sistema GNU" o autor, Richard Stallman, retoma o conflito "Sistema GNU" e "Sistema Linux". Para muitos pode soar como purismo e minúcia, mas para Stallman é assunto sério. Acredito que depois de lerem este texto todos vão entender por que sempre uso o termo GNU/Linux neste site.
Lembrando que este é um conteúdo livre obtido no CDTC.
Curso de Filosofia GNU - Parte 8 é um artigo original de Mind Bending
Magnun Leno : Curso de Filosofia GNU - Parte 8
22 de Abril de 2014, 21:40 - sem comentários aindaNo penúltimo texto do Curso de Filosofia GNU "Linux e o Sistema GNU" o autor, Richard Stallman, retoma o conflito "Sistema GNU" e "Sistema Linux". Para muitos pode soar como purismo e minúcia, mas para Stallman é assunto sério. Acredito que depois de lerem este texto todos vão entender por que sempre uso o termo GNU/Linux neste site.
Lembrando que este é um conteúdo livre obtido no CDTC.
Curso de Filosofia GNU - Parte 8 é um artigo original de Mind Bending
Magnun Leno : Curso de Filosofia GNU - Parte 7
21 de Abril de 2014, 20:24 - sem comentários aindaDando continuidade ao Curso de Filosofia GNU, neste outro texto (que pode ser classificado como um conto) Richard Stallman tenta nos levar a uma versão alternativa da terra (em 2096), onde o Software Livre fracassou e as empresas ditam as regras, controlando o acesso, leitura e empréstimo de textos, livros e artigos acadêmicos. Este artigo foi escrito em meados dos anos 90, e na época a ideia de e-books e leitores de livros eletrônicos ainda era remota.
Lembrando que este é um conteúdo livre obtido no CDTC.
Curso de Filosofia GNU - Parte 7 é um artigo original de Mind Bending
Helio Costa - hlegius : Test Double com Mock e Stub
21 de Abril de 2014, 7:35 - sem comentários aindaLembro-me bem da primeira vez que ouvi o termo. Era meados de 2009, quando li em algum lugar do GUJ sobre um "recurso" muito valioso nos Testes Unitários que era de fundamental entendimento para seguir adiante. Depois de ler algumas coisas na Internet, eu havia encontrado pelo menos três diferentes definições para Mock e Stub. Naquele momento eu descobri de que precisava de bibliografias-referência no assunto para acabar com aquelas meias verdades que pairavam sobre minha cabeça.
A primeira definição que encontrei era mais ou menos assim: "Mock você utiliza quando o valor de retorno importar; Stub nos demais casos." - ah, quantas vezes não "testei" software com esse mindset. Mesmo não entendendo bem o que era o tal valor de retorno, eu me aventurava e me forçava a fazer. Segui assim até ler pela primeira vez o livro Growing Object-Oriented Software, Guided by Tests, de Steve Freeman e Nat Price. Naquele momento minha cabeça explodiu e tudo fez mais sentido. Desde então, costumo definí-los da seguinte forma:
Stub é uma dependência da sua classe-alvo (Objeto em Teste), agindo como um substituto, evitando que a implementação real seja executada.
Explicação longa:
class Authenticator
def login(user)
return user.password == "123456"
end
end
describe Authenticator do
it "will login with valid credentials" do
user = double('User', password: '123456')
expect(subject.login(user)).to be_true
end
end
Repare no teste o user = double('User', password: '123456')
e repare que isto permite que eu simule um usuário "válido" (no exemplo é só o password bater com 123456) - ou seja, eu configurei minha dependência (User) para que o objeto Authenticator
pudesse ter um usuário válido. Um exemplo mais elaborado seria:
class Authenticator
def login(user)
if (user.admin? and user.has_confirmed_account?)
self.grant_permissions_to(user)
else
false
end
end
private
def grant_permissions_to(user)
# do something nice with our lovely user
end
end
class User
def initialize(sms_api: SMSApi.get_instance)
@sms_api = sms_api
end
def admin?
type == 'admin' # type could be an Database field mapped
end
def has_confirmed_account?
user.documentation_already_approved? and sms_api.cellphone_confirmed_for(self)
end
private
def sms_api
@sms_api # SMS Wrapper Injected via initialize (constructor) method
end
end
describe Authenticator do
it "will login with valid credentials" do
user = double('User', :admin? => true, :has_confirmed_account? => true)
expect(subject.login(user)).to be_true
end
end
Deixei o exemplo mais elaborado para mostrar o poder e a importância do Test Double: note que stubando o método User#has_confirmed_account?
eu simplesmente evito ter que lidar com o SMSApi
e com o documentation_already_approved?
, bastando eu ter feito meu double retornar true no has_confirmed_account?
. Imagina o trabalho que eu teria para configurar o SMSApi e o método de documentação aprovada em todo teste que eu precisasse chamar o método has_confirmed_account?
. Insano, né?
Graças ao double eu consigo focar no meu problema que é: o Authenticator#login
está, dado um usuário aceito por ele, conseguindo autenticar este user object?
Repare que o método grant_permissions_to(user)
não é stubado. Ele precisa ser chamado de verdade pois é um colaborador interno da classe-algo (Authenticator class).
Mock é ligeiramente diferente, precisa estar atento para entender as diferenças.
O Mock irá criar a expectativa de que aquilo que você definiu irá de fato acontecer. Se não acontecer, o teste falhará.
describe Authenticator do
it "will login with valid credentials" do
user = double
expect(user).to receive(:admin?).once.and_return(true)
expect(user).to receive(:has_confirmed_account?).once.and_return(true)
expect(subject.login(user)).to be_true
end
end
Assumindo o exemplo anterior, modifiquei apenas o teste, trocando Stub por Mock. Trocando quando e se, encontrar user.admin? e/ou user.has_confirmed_account?, substitua por true e true respectivamente para você (Authenticator#login) deverá chamar user.admin? e user.has_confirmed_account? (em qualquer ordem no método) apenas uma vez (once), e terá true e true respectivamente como resposta. Saímos de algo simples, para algo assertivo. Se por um acaso eu trocar o código de produção para:
class Authenticator
def login(user)
if (user.has_confirmed_account?)
self.grant_permissions_to(user)
else
false
end
end
end
O teste neste caso começará a falhar, reclamando a falta do user.admin?
.
Naturalmente, há regras e boas práticas para quando testar expectativas via Mock's e quando não. Sandi Metz abordou o tema neste Lunch 'n Learn.
Mock/Stub parciais (Partial Mocks)
Um recurso suportado pelo RSpec são os Partial Mocks. Há quem defenda o não uso deles (Prophecy @ PHPSpec, estou olhando para você!). Mock parcial, permite que você utilize um objeto real como dependência e mock apenas determinados métodos dela. Ainda continuando com o exemplo do autenticador, teríamos:
describe Authenticator do
let(:admin_user) { User.new(...) # faz alguma coisa para construir um Usuário admin? == true}
it "will login with valid credentials" do
expect(admin_user).to receive(:has_confirmed_account?).once.and_return(true)
expect(subject.login(admin_user)).to be_true
end
end
As diferenças aqui são:
- Não utilizamos um
double
do RSpec. Preferimos utilizar o User object de verdade, fazendo o que for necessário para criar/retornar um usuário administrador, ou seja, um objeto deUser
cujo o método#admin?
retornará true sem a necessidade de mudar o valor de retorno do método com stub. - Com isto, não precisamos definir no teste
it...
o retorno de#admin?
(pude remover a linha)
Com isto, ainda assim, eu mockei o #has_confirmed_account?
para continuar retornando true. Com isto, acabei fazendo um Mock Parcial: o método #admin?
é chamado de verdade e o #has_confirmed_account?
é mockado para retornar sempre true naquele teste.
Concluíndo
Mocks e Stubs são fundamentais para a construção do seu design e para seguir em frente com Test-Driven Development. Neste post, busquei mostrar, com definições mais simples, o que são ambos e dar foco nas suas diferenças. Mas não pense que sair mokando/stubando tudo é boa prática. Há situações onde você não deve mockar; há situações onde o mock aponta um possível problema de design - e então você precisa refatorar seu código e talvez criar uma nova layer na aplicação. Em todo caso, pratique muito o assunto e torne seu código mais legível, testável e plugável.
Happy Mocking ;)
Magnun Leno : Curso de Filosofia GNU - Parte 6
16 de Abril de 2014, 19:33 - sem comentários aindaEste texto foi extraído do site gnu.org, nele Richard Stallman — o criador do Software Livre — discorre sobre o projeto GNU e como surgiu o movimento software livre no MIT, tendo como objetivo inicial a criação do sistema GNU. Neste texto ele passa pelo desmoronamento da comunidade hacker até chegar nos GNU/Linux.
Lembrando que este é um conteúdo livre obtido no Projeto CDTC. Veja o restante deste conteúdo aqui.
Curso de Filosofia GNU - Parte 6 é um artigo original de Mind Bending
Magnun Leno : Curso de Filosofia GNU - Parte 5
15 de Abril de 2014, 7:34 - sem comentários aindaNesta quinta parte do Curso de Filosofia GNU, Djalma Valois Filho discorre sobre a nossa realidade no Brasil — em que setores do país o Software Livre deve ser aplicado, o objetivo de erradicar o analfabetismo tecnológico, o desenvolvimento de valores (solidariedade, cooperação e apoio mútuo) e o como/onde aplicar o retorno deste investimento — bem como suas próprias expectativas.
Lembrando que este é um conteúdo livre obtido no CDTC.
Curso de Filosofia GNU - Parte 5 é um artigo original de Mind Bending
Helio Costa - hlegius : Teste Unitário?
14 de Abril de 2014, 7:35 - sem comentários aindaPonto crucial para um possível futuro level up como Test-First e Test-Driven Development Design, o Teste Unitário de Unidade, em minha opinião, aparece como ponte entre fazer testes vs se frustar com eles.
O Teste Unitário de Unidade consta como principal forma de teste em várias literaturas que abordam o tema. Entender como criar testes de unidade para seu software é decisivo para adoção da divertida (e emprego keeper) forma de codificar software.
Primeiramente, é bom enfatizar que teste de unidade não é testar todo método de seu objeto, mas sim testar toda sua interface pública que contém regras de domínio. Vamos tomar o seguinte exemplo:
class User
attr_reader :name, :birthdate
def initialize(args)
# do something to build object
end
def sent_packages
end
end
No exemplo, temos o construtor (initialize
), o método sent_packages
além dos getters name
e birthdate
.
Para este caso, os getters retornarão o dado exatamente como ele foi inputado - prefiro o termo injetado - ou seja, os getters não contêm nenhum acréscimo para o negócio (software), por isto, não é necessário criar testes para eles, pois o Ruby tem o teste que garante o correto retorno para o dos valores em memória no attr_reader
. Se o attr_reader
já foi testado pela linguagem e o seu getter não tem nenhum acréscimo (formatação, contatenação, etc), não precisa-se criar teste para isto.
Um teste válido neste caso seria construir um usuário válido e verificar o retorno do sent_packages.
Vamos deixar a classe mais interessante:
class User
attr_reader :name, :birthdate
def initialize(args)
# do something to build object
end
def sent_packages
do_some_weird_stuff
# do more things
end
private
def do_some_weird_stuff
end
end
Ainda assim, deve-se continuar com os testes anteriores, pois não é recomendado testar métodos privados/protegidos isoladamente. Devemos sim, testar a interface pública - o método público - que chama o método privado que no exemplo acima é o sent_packages.
Aqui vale um parênteses importante sobre métodos privados: Sandi Metz afirma em seu livro que devemos levar todo método privado como um contrato que nos diz claramente: você pode até usar meu método privado, mas lembre-se: eu não garanto que ele continuará retornando o que retorna hoje numa próxima versão. Ele poderá até ser removido. Essa afirmação é válida, pois em Ruby você pode acessar qualquer método de um objeto mesmo os privados. Isso porque em Ruby, que segue o que eu já ouvi falar por "Smalltalk OOP", a orientação a objetos é pura e simplesmente troca de mensagens. Depois que adotei este mindset, a troca de mensagem e colaboração em minhas classes aumentaram significantemente.
Teste Unitário de Unidade não deixa haver colaboração entre outros objetos além do alvo do teste. O que isso significa em termos práticos?
class User
attr_reader :name, :birthdate
def initialize(args)
# do something to build object
@packages = []
end
def sent_packages
packages.map { |package| package.sent? }
end
def packages
# get user packages from somewhere (ORM, Repository, etc)
end
end
class Package
attr_reader :track_number, :post_date
def sent?
!!track_number
end
end
O sent_packages
acima pode te lembrar várias coisas: Rails ActiveRecord; Repository acessando dados de um DataMapper ORM ou uma simples iteração objeto-coleção.
Repare que a classe User
precisa falar com a classe Package
para verificar quais dos packages
foram sent. Não podemos deixar que o teste da classe User
permita que o objeto consiga falar com Package
. Ou seja:
describe User do
subject { User.new } # only to show how RSpec works to non-Ruby developers
context "when User relates with Package" do
let(:all_packages) do
[
double('Package', :sent? => true),
double('Package', :sent? => true),
double('Package', :sent? => false)
]
end
let(:sent_packages) { all_packages[0..1] }
it ".sent_packages" do
allow(subject).to receive(:packages).and_return(all_packages) # this is a stub!
expect(subject.sent_packages).to be_equal sent_packages
end
end
end
Mesmo que você não entenda Ruby nem as DSL's do RSpec, repare que no let(:all_packages)
eu utilizo um recurso chamado double
que nada mais é do que um objeto simples que pode ter qualquer nome. No caso, o chamei de 'Package'. O :sent? => boolean
diz ao double
que quando o método sent?
dele for chamado, deverá retornar true dos dois primeiros e false no último. Isso é equivalente a:
class Double
def sent?
true # and false in the last one.
end
end
Já o let(:sent_packages)
retornará uma parte da Collection all_packages
, no caso os dois primeiros doubles que retornam true no método sent?.
Finalmente, o it que é o teste, faz duas coisas: na primeira linha ele diz que User#packages
deverá retornar a collection all_packages
. Aqui aconteceu a mágica: com isso eu não deixo o User falar com o Package de verdade. Eu faço o User retornar uma collection que eu tenho total controle. Já explico o motivo disso.
Na segunda linha, eu crio uma expectativa que é: o método User#sent_packages
deverá retornar apenas os Package que tenham sent?
igual a true.
O motivo de utilizar o Stub (ali no allow...
) é fazer que o método packages
de User sempre retorne o que eu quero de uma forma controlada. Como o método package nada mais é do que um delegator para um Repositório ou mesmo um has_many :packages
do Active Record, isto é, uma dependência externa (outra classe), eu posso stubar ou mockar.
Dica: não mock/stub método de sua própria classe/objeto a menos que seja um delegator puro para outra classe, como no exemplo.
Dica polêmica: Only Mock what you own. Mock apenas o que você domina. Ou seja, classes criadas para o software que você está trabalhando.
O que você testou afinal?
Neste teste eu quero apenas saber se o User#sent_packages
sabe filtrar de todos os meus packages, apenas aqueles que foram sent e ele sabe!
def sent_packages
packages.map { |package| package.sent? }
end
Isso porque sempre devemos presumir que a classe relacionada já está testada individualmente. Em outras palavras: devemos assumir que Package
já está testada isoladamente (Teste de Unidade).
Porque não deixar User falar com Package?
Por quê isso que é Teste de Unidade. Se eu deixasse User
falar com Package
, deixaria de ser teste de uma unidade do sistema e passaria a ser duas - e não queremos isto, né? Pois isto criaria um acoplamento entre User
e Package
e não me evitaria a deixá-las isoladamente funcionando. Quando você não se atenta para esse tipo de coisa, você acaba deixando seu teste acessar o banco de dados ou aquele serviço REST ou SOAP. Isolamento como o acima, deve acontecer toda vez que o objeto que você está testando precisar falar com outra classe(objeto dela).
Concluindo
Teste de Unidade é um assunto longo, envolve muito mais do que simplesmente saber os do and don't, pois Teste de Unidade é questão de design.
Às vezes, você ficará tentado a testar mais de uma unidade, deixar o teste acessar o database, a API e outras classes do seu domínio. A prática leva a perfeição, dizem.
Final Alternativo (e melhorado)
class User
attr_reader :name, :birthdate, :repository
def initialize(args, repository: DomainRepository.new)
@repository = repository # hey, I am an injected dependency class
@packages = []
end
def sent_packages
packages.map { |package| package.sent? }
end
def packages
repository.packages_for(self)
end
end
class Package
attr_reader :track_number, :post_date
def sent?
!!track_number
end
end
Sem o ActiveRecord do Rails, o exemplo acima seria uma possibilidade: injeção do Repository via construtor ou setter. Desta forma, o mock não ficaria no método packages
do User, mas sim, no Repository injetado o que é epic win. Veja:
describe User do
context "when User relates with Package" do
let(:all_packages) do
[
double('Package', :sent? => true),
double('Package', :sent? => true),
double('Package', :sent? => false)
]
end
let(:repository) { double('Repository', packages_for: all_packages) }
subject { User.new({}, repository: repository) } # Stub Repository injected
let(:sent_packages) { all_packages[0..1] }
it ".sent_packages" do
expect(subject.sent_packages).to be_equal sent_packages
end
end
end
Com a adição de let(:repository)
que é injetado no subject {..}
, foi possível remover aquele stub allow(subject).to ...
do teste, deixando-o mais limpo, legível e plugável. Substituí uma injeção direta pelo stub da classe Repository. Este é o ideal, mas nem sempre é possível evitar que um Rails apareça com seu Active Record e deixe as coisas um pouco mais... complicadas. Não caia no engano de achar que apenas o Rails é vilão: até hoje não achei um framework ORM de Active Record que fosse plugável como um DataMapper é.
É isso sempre que buscamos com o Teste de Unidade: desacoplamento. Retirar coisas e torná-las injetáveis em nossas classes com o objetivo de passar e trocar mensagens entre os métodos de nossos objetos igualmente desacoplados. Isso é a base para criar seu design orientado a objetos.
Magnun Leno : Vocal: Gerenciador de Podcasts para o Linux
11 de Abril de 2014, 14:26 - sem comentários aindaEu não sei vocês, mas eu sou fanático por Podcasts. Atualmente, no meu android, tenho 17 podcasts inscritos sendo que destes apenas 8 nacionais (consequentemente 9 internacionais). Isso dá uma boa ideia de como seria a vida das pessoas que gostam de podcast sem um gerenciador.
Em outras plataformas (Windows e Mac OS) existem ótimas ferramentas para isso e, infelizmente, o GNU/Linux falhava nesse ponto. Até este momento…
Vocal: Gerenciador de Podcasts para o Linux é um artigo original de Mind Bending
Magnun Leno : Curso de Filosofia GNU - Parte 4
11 de Abril de 2014, 7:13 - sem comentários aindaEssa é uma parte delicada na Filosofia GNU, muitas pessoas não entendem porquê Richard Stallman implica tanto com a nomenclatura de sistemas operacionais. Muitas pessoas dizem que Stallman começou da forma errada, escrevendo as ferramentas antes do kernel. Mas se analisarmos o contexto histórico (a "decadência" do Unix), fazia sentido escrever primeiro as ferramentas para substituir aos poucos as ferramentas não livres, para posteriormente escrever um novo kernel.
Lembrando que o Curso de Filosofia GNU é um conteúdo livre obtido no CDTC.
Curso de Filosofia GNU - Parte 4 é um artigo original de Mind Bending
Magnun Leno : PyCon2014 em Montréal
10 de Abril de 2014, 12:39 - sem comentários aindaNa última Quarta-Feira (dia 09 de Abril) teve início a PyCon2014, desta vez localizada em Montréal, conferência oficial da linguagem Python que se estenderá até o 17 de Abril. Como das outras vezes o evento é dividido em Tutoriais (do dia 9 ao dia 10), Conferência (do dia 11 ao dia 13) e Sprints (do dia 14 ao dia 17).
Para quem ouve falar pela primeira vez da PyCon a seção Tutorial é a parte do evento onde ocorrem "aulas" e treinamentos, a seção conferência é a porção do evento que temos as palestras e as lightning talks e a ultima porção, os sprints é um período que os participantes de organizam em grupos e programam juntos, visando implementar alguma funcionalidade, resolver algum bug ou simplesmente colaborar com algum projeto Open Source.
PyCon2014 em Montréal é um artigo original de Mind Bending