Curso de Filosofia GNU - Parte 7

Dando 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.

GNU Reading

Lembrando que este é um conteúdo livre obtido no CDTC.

Curso de Filosofia GNU - Parte 7 é um artigo original de Mind Bending

Test Double com Mock e Stub

Lembro-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.

Test Double

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:

  1. 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 de User cujo o método #admin? retornará true sem a necessidade de mudar o valor de retorno do método com stub.
  2. 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 ;)

Curso de Filosofia GNU - Parte 6

Este 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.

GNU/Linux Starbucks

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

Curso de Filosofia GNU - Parte 5

Nesta 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.

Open Source

Lembrando que este é um conteúdo livre obtido no CDTC.

Curso de Filosofia GNU - Parte 5 é um artigo original de Mind Bending

Teste Unitário?

Ponto 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.

Vocal: Gerenciador de Podcasts para o Linux

Eu 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.

Vocal

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

Curso de Filosofia GNU - Parte 4

Essa é 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.

I Want GNU!

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

PyCon2014 em Montréal

Na ú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).

Pycon2014 Montréal

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

Entendendo o Heartbleed e Previnindo-se

Há um tempo nos acostumamos a pensar que, se estamos em um site com HTTPS (Protocolo HTTP encriptado por SSL/TLS) estamos seguros, assim como nossos dados. A criptografia SSL/TLS baseada em certificados supostamente previne qualquer tipo de interceptação de dados entre você e o servidor. Isso até aparecer o Heartbleed.

Heartbleed

O Heartbleed é uma vulnerabilidade séria que afeta a biblioteca e os softwares de criptografia contidos no OpenSSL. Esta falha permite que informações protegidas sejam roubadas (mesmo em circunstâncias normais de uso) em diversos serviços como web, email, instant messaging (IM) e algumas VPNs (virtual private networks).

Entendendo o Heartbleed e Previnindo-se é um artigo original de Mind Bending

Curso de Filosofia GNU - Parte 3

Na terceira parte do Curso de Filosofia GNU lhe serão apresentadas as 4 liberdades básicas que um software livre te garante e você irá entender porque a FSF (Free Software Foundation) luta para garantir esta liberdade.

Free Sfotware Word Cloud

Lembrando que este é um conteúdo livre obtido no CDTC.

Curso de Filosofia GNU - Parte 3 é um artigo original de Mind Bending