Obtendo o primeiro Test Pass

Depois de ficar maravilhado com as promessas de um mundo melhor código mais harmonioso, manutenível e desacoplado, vem uma curta pergunta que nos leva a nossa primeira Rua sem Saída: como fazer meu primeiro teste (do projeto) passar?

As minhas Ruas sem saída foram: “em um projeto novo, o que testar primeiro?” e, “em um projeto legado como fazer o primeiro teste?”

Vou criar um cenário hipotético, apenas para fins de apoio: você é contratado para fazer uma Extranet de Bike Shop. Os requisitos em alto nível são:

  • Área segura para somente funcionários/gestores logarem no sistema

Somente após login, a aplicação liberará acesso às features:

  • Cadastro de Marcas e Modelos de bicicleta;
  • Cadastro de bicicleta;
  • Cadastro de peças de reposição para uma (ou várias) marcas de bicicletas. – Pense nessas peças como peças de um determinado fabricante de carro: você pode comprar a peça “original” com a logo do fabricante do carro na peça ou a “paralela” feita pelo mesma empresa (por ex. Cofap, de amortecedores) mas sem a logomarca de nenhuma fabricante de carro, em um CarShop qualquer de rua.
  • Cadastro de peças genéricas (a tal da paralela).
  • Listagem de tudo isso que foi pedido.

Para efeitos de exemplos:

Marca: Caloi
Modelo: Urbe
Peça de reposição: Guidão Flat
Peça genérica: Cambio Traseiro Shimano TX 7 Velocidades

A peça genérica em nosso bike shop, em tese servirá para qualquer bicicleta que precisar daquele componente.

Começando: você escolhe a linguagem de programação, um possível bootstrap para subir um Hello Test no navegador e pronto. Está pronto para começar a fazer as coisas. Mas, e agora?

Sabendo por onde começar

A pergunta inicial não deveria ser “Por onde começar?” mas sim, “O que é mais importante no projeto?”. Seu cliente/chefe irá ver o projeto de tempos em tempos e você certamente terá dúvidas que só ele saberá responder. Enquanto o projeto não está pronto, o que é dispensável no projeto?

O sistema de autenticação é claro! Incrivelmente com todos que falei ou fiz essa pergunta (incluindo eu mesmo – sim, falo sozinho), o sistema de autenticação é escolhido como primeira coisa a ser feita. Pense bem. Para que raios a autenticação é importante agora? Ela evitará acesso indevido, mas se o software está em processo embrionário, com acesso limitado ao servidor e dados dummies, para que ele serve? Ignore-o por enquanto e foque no core do negócio!

Os pingos nos ís

Nat Price, sugeriu em seu livro criar um Teste de Aceitação e com este, criar seu primeiro Teste Unitário de Unidade. Seguiria criando os testes de unidade fazendo-os passar até que o teste de aceitação passaria também, finalizando assim aquela feature.

Ciclo do TDD por Nat Price

Eu já fiz essa abordagem, mas achei ela um tanto verbosa em casos onde o do teste de aceitação é muito parecido com o teste de unidade – até porque um software web é arquitetonicamente diferente de um software de missão crítica ou embarcado, por exemplo.

Atualmente, eu sigo apenas o processo interno, sem Teste de Aceitação ou/e Integração pelo simples fato de ser mais direto e não há nenhum efeito colateral no processo em si.

TDD Red Green Refactor ciclo

O Primeiro Green, Like a Boss!

Eu começaria pela Peça. Uma classe para identificar Peças “Originais” e outra para Peças Genéricas ou apenas uma?

O legal é esse tipo de decisão, pode ser adiado com Test-Driven Development. Vamos começar pelo mais fácil: uma classe Peça.

    @test
    everyGenuinePartBelongsToABicycle()

Assim como em Concessionárias de automóveis, há peças que são relacionadas ao veículo e por mais que sirva em mais de um modelo, a relação Parte-Veículo torna-se indispensável.

Algo assim, pode ser um começo:

    class BicyclePartTest {
            private subject = new Part();
            private bicycle = a_bicycle_mock_object;

            @test
            public everyGenuinePartBelongsToABicycle() {
                subject.setBicycle(bicycle)
                assertTrue(subject.isGenuine())
            }
    }

Ou seja, para Part (Peça da bike) ser Genuína (Original), a peça precisa estar relacionada a uma bike em específico. Esse simples teste já nos guia para nosso próximo:

    class BicycleTest {
            private subject = new Bicycle();

            @test
            public willContainGenuineParts() {
                subject.addGenuinePart(a_mocked_part)
                assertCount(1, subject.getGenuineParts())
            }
    }

Uma bicicleta deverá ter Peças. Neste caso, Peça Genuína devemos atentar que a relação é uma Composição, ou seja, a Parte(Part) não existe sem o Todo (Bicycle). Já sabemos também que:

    class Bicycle {

            public void addGenuinePart(Part genuinePart) {
                parts.add(genuinePart)
                genuinePart.setBicycle(this)
            }
    }

A relação bi-direcional deverá existir, conforme o teste do BicyclePart sugeriu lá no começo.

Montando a classe Part para que satisfaça o teste do BicyclePart, você já verá o Green na tua tela. Mais do que um, dois pontos verdes.

Eu já consigo ter uma próxima dúvida: em peças genuínas, poderá haver mais de uma peça por bike? Sim, não?

Sim! Pode-se optar por quantificar quantas peças daquela a bike tem e aonde elas ficam:

    class BicyclePartTest {
            private subject = new Part();
            private bicycle = a_bicycle_mock_object;

            @test
            public genuinePartMustHaveQuantityAndPosition() {
                subject.setBicycle(bicycle);
                subject.definePositionAndQuantity("front", 2)
                subject.definePositionAndQuantity("rear", 2)

                assertEquals(4, subject.getRequiredQuantity())
                assertEquals(["front", "rear"], subject.getPositions())
        }

            @test
            public everyGenuinePartBelongsToABicycle() { ... }
    }

Implementando o código de produção, o teste passará mais uma vez. 3 greens!

E Part não Genuína? Também terá posicionamento e quantidade? Uma porca do Cubo da roda, por exemplo são duas na frente (direita e esquerda) e duas atrás (direita e esquerda). Então:

    class BicyclePartTest {
            private subject = new Part();
            private bicycle = a_bicycle_mock_object;

        @test
            public partMustHaveQuantityAndPosition() {
                subject.definePositionAndQuantity("front", 2)
                subject.definePositionAndQuantity("rear", 2)

                assertEquals(4, subject.getRequiredQuantity())
                assertEquals(["front", "rear"], subject.getPositions())
        }

            @test
            public everyGenuinePartBelongsToABicycle() { ... }
    }

Neste caso nem precisei fazer outro teste. Apenas mudei o input e nome do teste anterior. Agora o teste diz que uma Part precisa ter quantidade e posição. Ok, mas e se eu não a definir? Uma vez que é obrigatório (..) uma ideia é mover para o construtor de Part, não? A simples ideia de fazer por steps, nos faz pensar sobre o negócio que estamos criando e consequentemente, criar um código mais legível e coeso (harmonioso, por exemplo).

Mas, mas…

E o Database Schema? A herança do ActiveRecord ou o Repository do DataMapper?
Uma coisa que eu faço – até com Rails – é criar POJO’s/PORO’s/POPO’s – Plain Old (Java | Ruby | PHP) Objects. depois que eu tenho uma relação mínima feita, daí sim eu crio as relações, os schemas, etc – pois eu sei que precisarei isolá-los e etc.

A autenticação ficou para uma fase mais madura do projeto. Poderá ser até mais para o fim, quando realmente ela for útil.

Concluindo

Seguir o de sempre e começar sempre pelo mesmo lugar não é algo justo de ser feito. Talvez você precise de uma Introspecção para rever algumas práticas (..)

Criar software orientado a testes é muito divertido, você precisa tentar, sério!

Challenge

Se você quer tentar, pegue esse simples projeto que citei aqui e tente implementá-lo em com sua linguagem do momento. Se quiser, pode me enviar por Github para te dar umas dicas. Se quiser também, podemos marcar uma conversa para discutirmos o design da aplicação. Será muito valioso para todos participantes. 😉

As ruas sem saída em Test-first

Uma vez entendidas as motivações de se fazer Test-first, mudar seu mindset para TDD, muitas coisas ainda restam apontar. Claramente, há um processo sugerido para conseguir se concentrar e tomar como guia quando chegar em becos sem saída trabalhando com Test-first.

Tão sabido quanto ao andar de bicicleta você fatalmente cairá uma hora ou outra, com os testes não é diferente: você irá sim chegar à ruas sem saída. Terá dúvidas, poderá sentir-se no escuro. Não pelo Test Driven Development per se, mas pelo fato de que você está saindo da sua zona de conforto; você está experimentando.

Na minha máquina (não) funciona!

pessoas que de tanto chegar à ruas sem saída, acabam assumindo que TDD e toda aquela coisa de design desacoplado são cartas viradas. É fato de que a ciência que envolve a computação é muito recente. Vivemos nosso momento paleolítico e haverão grandes cientístas (e tecnologistas) no futuro mudando a forma como lidamos com tais problemas, mas não podemos simplesmente desistir e dizer: isto não funciona. A ciência não funciona assim. Para poder dizer esta por**** não funciona você precisa dominá-la bem. Precisa apresentar todo um estudo defendendo seu ponto de vista baseado em dados, experimentos e outros estudos. Há artigos e não posts em blog provando a eficiência do Test Driven Development para projetos de software em nosso momento da história da computação. Obviamente se você pretende fazer apenas CRUD utilizando um framework One Size Fits All – que não conterá regra de negócio alguma, o Test-first torna-se desnecessário.

Rua sem saída

Rua sem saída – a.k.a ficar sem ideias ou não saber como prosseguir – não é algo ruim. Quando atingida, nos faz pensar sobre nossas decisões de design e como elas podem melhorar. Geralmente, ruas sem saída vêm acompanhadas de pesquisas, que puxam leituras que podem gerar debates de ideias que resultam em evolução profissional e better code.

Imagine quando começou a pedalar: você não tinha qualquer equilíbrio. Talvez não tenha utilizado rodas de apoio, mas ainda assim, não tinha equilíbrio sob duas rodas. Após várias idas à praça e várias voltas com sua bike, você começou a ganhar equilíbrio. Equilíbrio traz confiança que traz mais experiência, que te faz buscar andar mais longe e consecutivamente, mais quedas. Após crescer e ao começar a pedalar mais longe, você buscará técnicas de respiração para ir mais longe com menos esforço físico. Se guia sua moto, irá pesquisar técnicas de pilotagem e direção defensiva. Com Test-first o processo é exatamente o mesmo, adaptado para software: inicia em Test-first, busca por técnicas, experimenta; falha; pesquisa mais; tenta novamente; obtém o resultado, aprendendo a técnica; inicia o ciclo novamente.

Test-first é um conceito. Assim como design de software. Isto quer dizer que não existe uma resposta certa; mas sim, soluções aceitáveis. Para obter uma das soluções, você precisa:

  1. Ler livros, papers;
  2. Discutir com co-workers, listas de discussão;
  3. Praticar;
  4. Praticar;
  5. Praticar.

Equipando-se para se proteger em becos escuros

Como dito anteriormente, há algumas known tips que ajudarão você a manter-se focado na solução. Unclebob, já falou inúmeras vezes sobre isto em seu(s) blog(s), vídeos e palestras. Um conhecido é o Three Rules of TDD.

  1. Você deve sempre começar a feature, criando um teste daquilo que deseja obter como resultado – e o teste deve falhar.

  2. Você deve escrever apenas um teste de unidade por vez e este precisa falhar. Lembrando que não compilar é um erro também.

  3. Você precisa escrever apenas o necessário para aquele código passar. Lembre-se de que TDD é para preguiçosos.

Pode parecer um tanto dogmático, mas depois de entender os objetivos do Test-first, essa coisa faz sentido.

Testes são especificações. Um conjunto de especificações formam um software funcional. Working software é o objetivo que devemos ter. De nada adianta um monte de prática se ao final o software não é entregue como deveria. Sempre tenha isso em mente: esse teste precisa ter um propósito claro dentro do meu projeto. Se o teste não é claro, pode ser que você esteja caminhando para um beco escuro.

Red, Green, Refactor. É uma versão amigável das três regras do TDD. Teste falhando; Teste passando; Limpar seu código de produção para deixá-lo o mais legível e simples possível.

Simples? Métricas para Simples:

Single Responsibility Principle. Seu método, sua classe, seu módulo(mixin, trait, etc) precisa fazer apenas aquilo que se propôs a fazer. Métodos do objeto devem ser claros, objetivos.

Outra métrica é o DRY. Don’t Repeat Yourself. Fez copy-paste parcial ou totalmente de um trecho de código para utilizar em outro lugar? Duplicou o código.

Law of Demeter ou Tell do not ask: apesar do nome medonho, a coisa é fundamental estar registrada na tua cabeça: ao invés de perguntar algo de um objeto para com o retorno do método fazer alguma coisa, peça que esse objeto faça o que você quer utilizando seu input. Exemplo:

    public class Order {

      public void add(Produto itemComprado, int quantidade) {
        if (itemComprado.estaAVenda() && itemComprado.getEstoque().getQuantidade() >= quantidade) {
          OrderItem item = new OrderItem(itemComprado, quantidade, this);
        }
      }
    }

Ao invés de expor as particularidades do Produto e Estoque para o Order, podemos simplesmente fazer justiça com as próprias mãos:

    public class Order {

      public void add(Produto itemComprado, int quantidade) {
        if (itemComprado.temDisponivel(quantidade)) {
          OrderItem item = new OrderItem(itemComprado, quantidade, this)
        }
      }
    }


    public class Produto {

      public boolean temDisponivel(int quantidade) {
        return this.estaAVenda() && this.estoque.temDisponivel(quantidade);
      }
    }

Assim, Order#add agora passa apenas a adicionar produto à Order, sabendo apenas que precisa saber se tem estoque para tal. Como saber se tem estoque é problema do Produto e seu estoque não de Order.

Apesar de parecer óbvio, é um dos code smells que mais fiz e vi durante esse tempo. É natural falando/escrevendo, mas no meio do seu código, isso passa batido facilmente. Keep on track.

Concluindo

Técnicas e conceitos como S.O.L.I.D, precisam estar em nossas cabeças para evitarmos andar em direção aos becos escuros. Ruas sem saída, são parte do processo de aprendizado que são mitigadas com leitura e muita prática. Test-first não se aprende em um mês ou dois. Nem por isto, testing é uma coisa chata. Em minha opinião, os testes tornam o dia-a-dia muito mais divertido, desafiador e proveitoso.

Mais do mesmo

Gostou do assunto? Entre Julho e Agosto escrevi exclusivamente sobre problemas com as Ruas sem Saída em Test-first. Veja a lista:

Introspecção

Mudança. Todos temem à mudanças. E não seria diferente ao codificar software, não é mesmo?
Para que mudar se podemos continuar com nossa atual rotina simples e praticamente automática? Aquela coisa de chegar no trabalho, fazer o de sempre, como aprendeu no tutorial há tempos atrás ou que viu o ex-chefe fazer e até hoje executa assim sem questionamentos.

Em software isso recebeu um nome no livro The Pragmatic Programmer. Caso o cenário acima encaixe no seu code-style, você está programando por coincidência. Gosto do termo em português, pois ele é duplo sentido. Além de indicar o estado profissional acima, ele também implicitamente diz que você está na área por um mero acaso. * O livro não fala disso. Essa segunda parte é totalmente de minha autoria. Pode parecer xiita, mas te convido a refletir sobre isso mais a fundo.

Ao entrar no seu modo automático e passar a fazer coisas sem se questionar o porquê está fazendo (ou fazendo novamente) aquilo, você está programando por coincidência. Seguir aquele fluxo toda vez que depara com um problema passado sem questionar-se o motivo e se há melhor alternativa, você está programando por coincidência. Se, você assume coisas – isso vale muito pra teste – por exemplo que o erro X é causado pelo Y, simplesmente porque você acha que isto ocorre, você está programando por coincidência. Neste caso você não deveria assumir, mas sim provar que o problema é aquele mesmo.

Saindo da coincidência

Uma das formas de evitar esse ciclo de tédio e comodismo é justamente fazer Test-First. Explico os motivos:

  1. Lego Effect: como comentei no Teste Unitário?, começar pelo teste fará você pensar em como aquela funcionabilidade que pretende fazer irá se relacionar. Isso te faz pensar mais e consequentemente, dúvidas virão e você buscará saná-las (livros, pesquisa na Internet, Twitter, outros devs, projetos Opensource).
  2. Ah, a preguiça! Os testes te ajudarão a achar o menor caminho possível para aquela implementação. (teste é coisa de preguiçoso, lembra?) Isto te fará repensar técnicas. Com a prática de novas técnicas, você evoluirá como desenvolvedor.
  3. Não mais dúvidas sem resposta! Com teste, você pode testar não apenas o código de implementação mas também as suas suposições. Pensamentos do tipo: será que isto pode funcionar desta forma? serão mais frequentes, uma vez que o espaço entre o será que… e a resposta são apenas escrever um assertion simples e executá-lo.
  4. You on Rails. A prática do Test-first te mantém na linha de fazer o que realmente importa. Sem suposições vazias; Sem achismo; Você se antecipa dos problemas que poderá encontrar. Estar alinhado em fazer o que importa te torna introspectivo.
  5. Test & fail. Test & Learn.
  6. Não sendo mais telespectador. Você no controle! Não mais sendo guiado por processos e rotinas sem saber o motivo de estar fazendo aquilo daquela forma.

Questione-se; Reflita; Pense um pouco mais sobre sua forma de fazer as coisas. Isso é realmente a melhor forma de se obter o resultado que deseja?

Recentemente peguei meu Card Deck que ajudei a fundar (?) no Kickstater. Um dos cards que mais me chamou a atenção, até pelo fato de eu já ter feito isso anteriormente é o Remove your BEST idea.

Remove your best idea

Conseguiu achar uma solução para o problema? Ótimo! Ela poderá ser seu backup caso as coisas dê muito errado. Empenhe-se para achar uma outra melhor ideia. Vale muito a pena esse exercício.

Concluindo

Test-first é uma das formas de acabar com a programação por coincidência. Test-first entretanto não irá te salvar de todo o mal do mundo – mas posso afirmar, por experiência própria que ele te coloca on track novamente.

A propósito, já leu o The Pragmatic Programmer: From Journeyman to Master? Permita-me dizer que você deveria se ainda não o fez pelo menos uma vez.

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.

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

Concluindo

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 😉

Teste Unitário?

ad: Há muito mais sobre a temática Test-First aqui no blog. Dê uma olhada nos posts mais recentes e assine o feed dos posts 😉

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.

O que Test-Driven Development não é

Uma dúvida muito pertinente em engenharia de software é sobre Test-Driven Development, TDD para os mais chegados. Há resistência por parte daqueles que não conhecem – ora, também não é para menos: o nome remete a Teste e teste remete a um processo da área de Verificação e Validação.

Após ouvir o developer dizer nunca fez e nunca viu “fazerem TDD” e suas argumentações sobre o porque isso não funciona, costumo perguntar uma simples pergunta: para você, o que é TDD ?

Até o momento as respostas foram uma derivação de: “Serve ahn… pra você verificar se seu software funciona (..) serve para evitar bugs”. Ora, não é para menos que você desacredita – e por isso nunca tentou – na ideia.

Redução de bugs é um efeito do TDD e não seu objetivo. Alguns já não definem mais a sigla como Test-Driven Development, mas sim como: Test-Driven Design o que na minha opinião é muito mais descritivo para os “novatos no assunto”. Este novo nome deixa tudo mais claro: TDD é sobre a construção do seu design. Pronto. Só isso.

Partindo do princípio que você saiba o que “design” significa em engenharia de software, aposto que está repensando em tudo que você argumentava (se é que) contra a adoção do estilo de desenvolvimento. Explicado o que é o Test-Driven Development Design há ainda outras barreiras a ser vencidas. Vamos a elas.

1. TDD consome mais tempo

Depende. No início é óbvio que você irá mais devagar, pois estará aprendendo algumas coisas enquanto tenta implementar uma feature:

  • A técnica de codar seguindo TDD;
  • A técnica de codar Testes Unitários de Unidade;
  • O framework de teste;

Codificar seguindo o Test-Driven Design (vou repetir isso inúmeras vezes para fixar), após estudar e pegar prática, você irá produzir mais do que antes. Fato. Além disso, provavelmente o deixará pensando bastante sobre pontos como:
como devo começar meu teste? O que devo testar? Como testar? e, até onde testar?

Note que “testar” neste contexto significa: verificar que o método está fazendo o que se espera que seja feito; Apesar de parecer óbvio, é melhor ter isso sempre em mente.

Teste de Unidade é um ponto importantíssimo para conseguir seguir em frente com o Test-Driven Design. Isso dá um post dedicado, por isso podemos discutir sobre o que não é Teste de Unidade.

Ele não “testa” necessariamente todos os métodos da sua classe. Um erro comum é achar que Teste de Unidade significa testar todo getter e setter de uma classe. Errado.
Quando estiver testando um método de uma classe e este método chamar outro método de outra classe, você não pode deixar essa chamada acontecer “de verdade”. Aqui entra o assunto Test Double – double, mock e stub. Também digno de outro post. Quando eu digo outro método de outra classe, estou literalmente dizendo que:

class Foo
  def do_something(another_class: MyClassDependency.new)
    # …
    another_class.find_bar(…)
  end
end

o MyClassDependency#find_bar precisa ser “mockado” ou “stubado”, pois devemos sempre partir do princípio de que ele já foi testado isoladamente e o find_bar irá retornar o objeto/valor corretamente. Por isso o Mock/Stub. (vide imagem do post ;])

Não devemos testar métodos privados diretamente. Dizem que há exceções, realmente podem haver, pois design é questão de experimentar e evoluir, porém eu em particular nunca testei método privado – mesmo em algumas situações eu ter insistido e quase feito.

O motivo é muito simples: devemos encarar nossos métodos privados como versões instáveis da nossa API. Considere toda classe do seu projeto, como uma API: métodos públicos são o canal de comunicação com a classe que tem suas interfaces (nome do método, parâmetros e retorno) estáveis. Já os métodos privados da classe, são nossos métodos instáveis, ou seja, estamos informando ao cliente (outra classe do projeto) que aquele método poderá mudar sua interface de uma versão para a outra sem aviso prévio. Isto não quer dizer que seus métodos privados não serão testados. Muito pelo contrário. Você precisa testar o método público da sua classe que utiliza o método privado em questão. Com isso, você garante não só que seu método instável (privado/protected) está funcionando, mas também que ele está bem relacionado com o(s) método(s) público(s) que o utiliza. E é perfeitamente normal sua classe possuir alguns métodos privados. Não tenha medo de utilizá-los, sempre pensando que um método deve ter apenas uma responsabilidade – um motivo para mudar.

Novamente: Teste de Unidade não deve chamar métodos de classes colaboradoras (igual exemplo acima). Ou seja: não devemos chamar nossa API/abstração de banco de dados, nem aquele Webservice de Pagamento/Notificações, nem o seu Mailer, nem seu Filesystem, etc. Você sempre deverá Mockar ou Stubar essas “dependências” da sua classe.

2. Eu gosto de “Testar” depois.

Há quem diga que já tem em mente o que precisa fazer para aquela feature funcionar e que por isto, prefere fazer o código de produção [1] primeiro e o teste depois.

Essa é fácil: se for para fazer o teste depois, você não estará construindo o design da sua aplicação. Estará perdendo tempo.

Testar depois tem dois problemas: a) perderá a chance de desacoplar suas classes, pois acredite: TDD faz você pensar mais sobre como fazer e isso incrivelmente nos faz mais produtivos, pois você resolverá os problemas de forma mais simples. b) você fará o teste assumindo que sua classe/método está correto e funciona, com isso criará o que chamam de Teste Viciado que em outras palavras quer dizer: você faz seu teste satisfazer seu código de produção, porém seu código de produção não garante que o teste está correto.

Dica simples: não deixe para testar depois. Crie o costume de fazer o teste antes!

[1]: Código de produção é o código que você deploya.

3. Minha equipe/co-workers não tem interesse pelo assunto.

Você não precisa esperar seu chefe instaurar a obrigatoriedade do Test-Driven Design. Não espere que ele saiba das vantagens disso. Aproveite o momento para você começar a espalhar a ideia na equipe.

Recentemente entrei numa equipe que não faz testes de unidade, tão pouco TDD. No começo, fiquei observando a reação das pessoas sobre Test-Driven Design e suas queixas sobre a possibilidade de fazer. Minha meta era em um mês conseguir mostrar o que a técnica de teste poderia resolver nos projetos e o quão empolgante é codificar com Test-First. Passado este um mês, eu já tinha mostrado TDD in Action para três desenvolvedores que além de ficarem convencidos e animados com a ideia, passaram a defendê-la. Eu acredito que mudanças de cultura deste tipo, devem sempre ser bottom-up, dos developers para os gestores. Muito improvável que seu gestor não aprovará a ideia se você se propuser a explicar para que o Test-Driven Design é executado em todo o mundo.

Concluindo

Test-Driven Development Design é sobre como construir seu design de forma simples e desacoplada. Sem bullshits, sem bala de prata ou santo graal. Adotar Test-Driven Development é evoluir sua forma de fazer software de uma forma muito rápida.

Migrando aplicações sem precisar migrar de emprego

Migração. Eis uma palavra que pode assustar vários developers que iniciam suas aventuras no universo devops[^1]. O que realmente isso significa? O que isso traz? E melhor, como executar uma com maestria e baixo risco de colocar tudo a perder?

Cenário

Você tem um software funcionando, porém, recentemente ficou sabendo de novas funcionabilidades que irão totalmente contra ao que você tem atualmente. Nessa hora você pensa: mas como eu irei ajustar todos os dados gerados até agora? Como migrarei contas de usuários, informações privadas e adicionar novas constraints às contas que até agora não precisavam disto? Pois é, amigo. Welcome aboard!

Eu realmente preciso migrar ?

A primeiríssima coisa necessária é: entender o real motivo em seu cenário atual. Isto é: aonde está e para onde irá o sistema com as novas features. Elas realmente confrontam seu system state atual? Algumas vezes, percebemos que a nova feature requer apenas novos dados dos quais nós (developers, devops e curiosos) não teremos como inputar para toda a base atual. Isto resulta em uma tarefa simples: criar um plano de atualização cadastral. Você pode utilizar-se de notificação por e-mail ou/e forçar um update após o login do seu cliente. Sem modificações, sem migrações. YAY!

The rockin’ plan!

Ok, infelizmente não há como executar um update via usuário. A área de operações forneceu uma planilha imensa com os dados e não temos como inserir um a um. Precisaremos migrar dados.

Tudo começou…

… com um plano! Quanto mais refinado, melhor – ou devo dizer: quanto mais refinado, menor a chance de dar tudo errado às 5am ?!

O ponto é: você precisa traçar um plano. É necessário ter começo, meio e fim. Nas primeiras linhas escritas, haverão vários vazios vertendo dúvidas e incertezas. Isto faz parte e você vai preenchendo as lacunas vazias com o passar dos dias. Conforme vai escrevendo seu plano em (uma) folha de papel, indagará uma coisa importante: o que eu farei se alguma coisa der errado?

Você não fará!

Nem sempre você terá um rollback passível de execução – e isto é normal!
Neste momento você sentirá frio, calor, suor e medo, muito medo. Perceberá que é tudo ou nada. É aí que você percebe que você precisa garantir 100% que seu plano funcione.

Automatize tudo que for possível

Parte importante do seu plano é criar tantos scripts quanto possível para realizarem a tarefa para você. O ideal é que durante a migração, você apenas rode uma sequência de comandos que farão todo o trabalho para você. Assim, a chance de esquecer alguma coisa ou fazer alguma coisa não esperada, reduz para menos de 10%.

Rubistas já começam a pensar em Rake Tasks; PHPístas começam a pensar em alguma espécie de recipe – esse é o espírito! 🙂

Defina a ordem de execução

Devo migrar as contas de usuário antes ou depois de definir as categorias dos clientes? Crio a conta deles no ambiente novo ou deveria atualizar a base existente que não contém o plano do usuário?

A ordem de execução é importante e deve ser seguida à risca. Não é possível criar a conta do cliente no novo ambiente o qual requer que todos tenham planos, correto? Logo, a definição do plano por cliente vem como premissa do primeiro item.

Anote em papel todos os steps-to-heaven que você precisará executar. Isto ajudará você a entender melhor da onde está saíndo e para onde chegará após a migração. Neste processo, você encontrará scripts faltantes e até scripts desnecessários.

Practice, Practice, Practice.

Tenho o plano, os scripts e a ordem. O que falta?

Como diria Chad Fowler em seu livro The Passionate Programmer: “você não pode chegar no trabalho para executar algo que nunca tentou antes. Você precisa praticar antes do show acontecer.”

Você precisa praticar. Praticar MUITO!

Seus scripts funcionam individualmente? E dentro do contexto de migração? Se algum falhar ou rodar pela metade, poderei rodá-lo novamente sem prejuízo aos dados já migrados? Atomicidade aqui é vital para uma migração. Você pode e deve considerar o pior cenário: Internet lenta, timeout em conexão com o servidor, etc.

Execute e reexecute os scripts por várias vezes ao longo do período pré-migração. Rode com poucos dados e com muitos dados armazenados. Acompanhe e verifique se está tudo funcionando como deveria. Não deixe de testar também no seu ambiente de staging. Melhor falhar cem vezes agora do que uma vez em produção, não é mesmo?

Momento da migração

Chegou a hora. Você acordou pontualmente às 4hs da madrugada. Parou os serviços que serão afetados e saiu atualizando os softwares de dentro pra fora, ou seja, do mais interno para o mais público dos sistema afetados pela migração. Você visualiza aquela folha com seu plano toda rabiscada e reeditada, já decorada pela sua mente e então começa a migrar.

Caso tenha executado toda a via sacra, provavelmente não terá problemas dos quais não consiga facilmente contornar – isto se houver problemas!

Ambiente migrado, paz interior reestabelecida. Moment of Glory, developer!

Para fechar, acompanhe atentamente o dia de trabalho dos seus usuários para garantir que tudo está como deveria. Se você estiver apoiado com logs ativos (Loggly, Papertrail[^2]), melhor ainda! Há a possibilidade de configurar alertas para você receber em tempo real possíveis traces com problemas enfrentados pelo seus usuários por e-mail ou Campfire[^3]!

Se você não utiliza um recurso assim, recomendo e muito que o faça o quanto antes!

Recipe para migração

Eis um resumão:

  • Verifique a necessidade da migração;
  • Defina um plano de ação;
  • Codifique scripts que farão todo o trabalho por você, faseando-os por atividade dentro da migração;
  • Crie uma ordem de execução dos scripts. Há precedência e pré-requisitos a validar;
  • Pratique muito antes de tentar em produção;
  • Execute em produção, e;
  • Acompanhe o dia dos seus usuários, prestando suporte passivo e ativamente.

[1]: devops é um desenvolvedor de software que assume papel/chapéu de operação do servidor de aplicação.
[2]: Papertrail é um SaaS que salva vidas quando o assunto é logrotate pesquisável, trackeável e alertável via e-mail, Campfire e outros.
[3]: Campfire é um SaaS para chat colaborativo muito simples e útil para se trabalhar em equipe. Uso e recomendo!

Ruby e as variáveis de classe

Recentemente percebi algo muito peculiar no comportamento do Ruby quando trata-se de variáveis de classe. Sabemos que:

class Foo
  @@bar = 'baz'

  def self.bar
    @@bar
  end
end

p Foo.bar #baz

Porém, certa vez, aconteceu um caso peculiar.

class User < ActiveRecord::Base
    @@default_user_locale = :en

    def default_user_locale
         # …
    end
end

p User.first.default_user_locale

Isso, conforme o esperado, deveria retornar en, mas acredite, retornou outra coisa.

O fato

Até não ter ideia do que ocorrera, resolvi pesquisar. Encontrei pessoas afirmando que: "@@default_user_locale era uma palavra reservada do Rails" =S
Pode ser qualquer coisa, menos isto, afinal estamos falando de uma classe!

Daí, lendo o livro Eloquent Ruby, me deparei com o tal problema. Não é culpa do Rails (ou POG do Valim), e sim, um comportamento maluco do Ruby. O dito cujo, trata variáveis de classe como variáveis globais. Logo, se você possuir uma herança, onde em ambas classes a constante for definida, o valor da última classe a ser carregada definirá o valor da variável da classe.

Em termos práticos:

foo.rb

class Foo
    @@size = :small

    def self.size
     @@size
    end
end

bar.rb

class Bar  small

A solução

A sugestão para sanar este problema, de acordo com o autor do livro é utilizar variáveis de instância com métodos de acesso estáticos. Desta forma, o valor nunca será sobreescrito caso uma classe pai tenha definido a mesma variável internamente. Ficaria assim:

class Foo
    @size = :small

    def self.size
        @size
    end
end

Fica o alerta para evitar uma noite perdida 😉

Traits em PHP. Herança Horizontal

Post revisitado em junho/2014. Tudo que está aí funciona na versão atual PHP 5.5.x. Enjoy 😉

Antevejo que no futuro, o PHP como uma linguagem iterativa e incremental que é, adicionará ao seu núcleo um recurso muito valioso na categoria orientação à objetos.Antevejo ele, os Traits. E mais, consigo prever que será algo assim:

ola()} trait !";
}
}

$ola = new Mundo();
$ola->world();
// Olá trait !

Ok, não sou bidu. Realmente os Traits estão na lista do possível-futuro PHP 5.4 e eu, baixei a build de 31/03/2011 para testar.

Traits

O PHP não suporta herença multipla – amém – e por este motivo, às vezes ficamos limitados em algumas decisões de projeto (design). O maior problema neste ponto é que toda herença até agora (PHP 5.3.x) é vertical, ou seja, se eu precisar de um nível de abstração diferente no meio do processo, ou precisarei adicionar a nova abstração e reescrever tudo abaixo dela, ou ainda, terei que duplicar o código pois não conseguirei satisfazer a herença.

É nesse ponto que os Traits aparecem. Diferentemente da herença (vertical), os Traits possibilitam-nos criar herenças horizontais.

Exemplo, dada a necessidade: haverão Funcionários, Supervisores, Gestores e Diretores no sistema e estes possui em comum o fato de receber salário. (capitalismo). Uma possibilidade seria:

Pessoa > Funcionario
Pessoa > Supervisores
Pessoa > Gestores
Pessoa > Diretores

Tudo perfeito. Porém, chega outra necessidade: Gestores e Diretores tem a capacidade de demitir. Gestores precisam gerenciar cronogramas, assim como os Supervisores fazem.

Pessoa.demitir(pessoa) não faz sentido, pois afetiria todos.
Gestor.demitir(pessoa) e Diretor.demitir(pessoa) atende, mas duplicaria o código.

Pessoa > Supervisor > Gestor
Pessoa > Diretor

Shi, apareceu outro nível hierarquico. Traits auxilia exatamente neste ponto ! Vejamos:

Trait Cronograma

Trait Evil (que contém recursos para demitir uma pessoa)

Pessoa > Funcionario

Pessoa > Supervisor : Cronograma

Pessoa > Gestor : Cronograma : Evil

Pessoa > Diretor : Evil

Note que o Pessoa > Supervisor : Cronograma – os dois pontos para indicar o Trait são de minha autoria – era o Pessoa > Supervisor antigo. Isso porque as atribuições para gerenciar cronogramas foram passadas para o Trait.

Neste pequeno exemplo, o Trait nos auxiliou com um problema de domínio. Acredito que ele também aplique-se e muito bem para problemas onde temos domínios transversais, pois, nele ficariam as atividades do outro domínio pode o domínio principal teria acesso aos recursos sem quebrar a SRP.

Traits em PHP

Como disse no começo, o futuro-provável PHP 5.4 virá com o Trait. Por enquanto essa implementação funcionaria:

definirPressao(self::$ATRASADO);
        $this->youTube();
    }

    private function youTube() {

    }
}

class Funcionario extends Pessoa {
}

class Supervisor extends Pessoa {
    use Cronograma;
}

$colaborador = new Funcionario();
$controlador = new Supervisor();
$controlador->cobrarDe($colaborador);

Os Traits possuem boas capacidades com métodos de instância e estáticos. Vamos explorar um pouco mais:

sayHello();
$view->sayWorld();
// exibe: Olá Mundo

Pegando informações da Classe

eu();
// exibe: Foo

Usando Variáveis de instância da classe:

hello} world";
    }
}

class Hello {
    private $hello = "Hello";
    use World;
}

$hello = new Hello();
echo $hello->help();
// exibe: Hello world

Modificadores de acesso dos traits na classe

ola();
// Fatal Error....

Modificadores de acesso com alias para nome de método

aloha();
// Olá mundo

Há outros vários recursos como métodos abstratos nos Traits, precedência, métodos estáticos, late static bindings, métodos mágicos, alias para nome de método do Trait ao usá-lo na classe. Para Rubistas ou Scalistas (?) as possibilidades são as mesmas dos Mixins e Traits de Ruby e Scala respectivamente.

RFC do PHP Trait
PHP doc do Trait. PHP >= 5.4

Você assinou seu código hoje?

Quem utiliza-se ou já utilizou da annotation @author no cabeçalho de seus códigos levanta a mão !

Annotation @author

Desde que comecei a programar no início do segundo semestre de 2004, eu utilizo desse tal de arroba-aútor. Já vi algumas variantes dela, por exemplo:

Annotation @author Augusta edition

Há ainda aqueles que detalham no cabeçalho sua pog contribuição.
Sinceramente eu nunca fui pesquisar se há uma convenção na utilização deste annotation. O fato é que meu uso com isto é descomplicado:

  • Insiro meu nome em novos arquivos;
  • Adiciono meu nome abaixo do último se eu fiz alguma adição ao arquivo, ou;
  • Não adiciono quando faço pequenas modificações sem acrescentar algo de valor ao então domínio/objetivo/proposta do arquivo.

Mas mesmo assim, sempre colocava no formato **Nome completo **

Porém, não pela primeira vez vejo um developer defendendo o não uso deste annotation. A última vez foi numa discussão – no bom sentido – entre @sergioprado e @lfamorim. Com isto, fui perguntar a eles – e alguns outros mais – a opinião pessoal em relação ao tal annotation. Confira:

Eduardo Guerra@emguerra
Obviamente se o uso de @author nas classes for uma prática da equipe irei segui-la sem problemas, mas se a escolha for minha opto por não colocar. Esse tipo de marcação no meu ponto de vista atrapalha a propriedade coletiva do código por determinar explicitamente os autores da classe. No caso de erros na classe, sempre haverão dedos apontando o autor como culpado, mesmo se não foi ele quem introduziu o problema.

Como toda documentação não-executável, pode deixar de ser atualizado com a evolução de um projeto e muitas vezes o autor original não é mais quem mais contribuiu para a classe no seu estado atual. Adicionalmente, sou a favor de manter o código o mais limpo possível e esse tipo de coisa acaba deixando o código de todas as classes mais verboso.

Cainan Costa@sryche via Jabber
[20:03:08] cainan.costa: não faz sentido, porque o VCS já mostra =P
[20:03:16] cainan.costa: quem é o maluco que fez a parada
[20:03:25] cainan.costa: aí tu usa um git blame
[20:03:33] cainan.costa: e já sabe quem encher de porrada

Lucas Fermando Amorim@lfamorim
Hoje em dia a tag @author é tão obsoleta quanto respeitar um limite de colunas para seu código, mas ainda assim acho elegante e talvez até relevante.

Fica muito mais fácil para aqueles programadores que não usam a IDE XPTO verem o criador de determinado arquivo, método ou função sem ter que recorrer a um programa. É repetitivo você pode alegar, mas eu posso retrucar que a tarefa pode ser automatizada.

Eu aprecio muita essa tag, pois a partir dela posso usando minha humilde IDE identificar o criador de determinados códigos, antecipando os vícios do autor e me adaptando para a leitura. Tudo isso sem abrir a shell e digitar N^C comandos.

N = Familiaridade com o programa X de controle de versão.
C = Commits anteriores.

Christian Benseler@chrisbenseler
Tenho uma visão que o negócio é “mais embaixo”. Antes de tentar assinar um script como seu, o desenvolvedor tem que se preocupar com outros assuntos mais importantes: é um projeto pessoal ou colaborativo? Está prestando serviços a alguém, outra empresa? Qual a política do seu cliente? Qual a versão do script? E qual o tipo de licensa que será adotada?

Só assinar o script não vai fazer a menor diferença se não tiver essas informações. E não vai fazer nenhuma diferença se a comunidade não parar de copiar o script feito por A, retirar os author notes do script e redistribuir o script sem dar esses créditos e/ou sem levar em conta o tipo de licensa adotado.

Carlos Duarte do Nascimento@chesterbr
Antes de mais nada, você se refere a qual linguagem? Em Java, @author pode significar tanto uma anotação em si (e aí depende do contexto), tal como:

@author("chester")

public foo(int bar) {

quanto (o que eu imagino que você queira dizer) a anotação feita em um doclet, i.e.:

  /**
   * Um elemento coberto por JavaDoc.
   * @author chester
   */
    public foo(int bar) {
    …

No segundo caso, eu gosto de @author devido à facilidade em lidar com a autoria coletiva: ela permite anotar classes inteiras ou métodos/atributos particulares; você pode colocar múltiplos autores, e o Eclipse pode ser facilmente configurado para adicioná-la automaticamente. No PHP, se me recordo, é possível usar algo equivalente (phpdoc) em cabeçalhos de código, mas não sei se é válido colocar em métodos/atributos, ou se é possível ter múltiplas declarações no mesmo escopo.

Em qualquer caso, vale notar que sistemas de controle de versão como svn e git são mais eficientes para descobrir quem efetivamente contribuiu com uma classe/método do que esse tipo de tag. Além disso, elas não são suficientes para notas de copyright (ou seja, teriam que ser replicadas apropriadamente para o correto licenciamento do código, independente da licença utlizada), o que limita de fato sua utilidade prática.

Sérgio Prado@sergioprado
Há muito tempo atrás, as ferramentas disponíveis aos programadores eram escassas e limitadas.

Já programei em Assembly com um DOS rodando num x86 e monitor CGA de 80 colunas x 25 linhas

Nesta época, ferramentas de controle de versão não eram tão comuns, nem monitores de 21 polegadas. Foi então que surgiram alguns paradigmas. Eram consideradas boas práticas de programação usar no máximo 80 colunas por linha de código (limitação causada pelos monitores da época), e incluir informações no cabeçalho do arquivo como @version, @author, @date, @changes etc.

O problema é que para muitas pessoas estes paradigmas perduram até hoje, por herança, por gosto (ou mazoquismo mesmo). Qualquer ferramenta decente de controle de versão pode te trazer, desde sua criação, todo o histórico de alteração de um arquivo. Além de ser mais seguro e confiável. Se eu usar no código-fonte “@author pica-pau”, grande parte das pessoas não irá perceber, porque ninguem lê o cabeçalho do arquivo. Também não existe garantia de que o nome documentado no código-fonte é realmente o do desenvolvedor. Já com uma ferramenta de controle de versão esta garantia existe.

Eu mesmo usava @author nos cabeçalhos uns tempos atrás, mas parei de usar depois que comecei a criticar alguns paradigmas que tinha. É tudo questão de delegar para ferramentas o que pode ser automatizado e buscar novas soluções para os mesmos problemas.

Fábio Miranda@fabiolnm
Não uso por pura falta de hábito – nunca trabalhei em um projeto onde essa prática fosse adotada.
Mas acho interessante, não só o autor, como o email / github tb.

Pode ocorrer de ser necessário dar manutenção em determinado código cuja compreensão não seja trivial, e a possibilidade de contactar o autor, mesmo que remota, deve ser considerada e viabilizada. Não vejo razão para ser contra.

E pra finalizar, os comentários fundamentados de Hanneli Carolina @hannelita – sobre o uso de @author e muito mais:

Bom, vou dar minha opinião sobre comentários no código antes de qualquer coisa…. Também utilizarei algumas metáforas e exemplos reais.

Eu já trabalhei com códigos de sistemas legados – Mainframes e sistemas bem antigos – , envolvendo C e Assembly. Vou contar uma coisa – quando abríamos um código e víamos infinitas linhas de comentário, já sabíamos que vinha coisa ruim pela frente. Tudo bem para aquele tipo de código sempre achei necessário comentar – Há algumas subrotinas do inferno que precisam de explicação. E o cara que fez isso tem idade pra ser meu pai, já faleceu ou se aposentou. Portanto, códigos assim são “legíveis” graças aos comentários deixados para trás. Já peguei código em Assembly sem comentários. E fiquei quase 10 dias só pra entender por cima o que o programa fazia.

Mas atualmente estamos em uma época onde encontramos linguagens extremamente expressivas, como Ruby e Python. E pra ajudar ainda tem uns framewroks tipo Sinatra, incorporando DSLs. Ou seja, hoje é possível escrever códigos que HUMANOS são capazes de ler. (humanos comuns, quis dizer hehehe). Se você escreve códigos em Ruby que humanos não entendem, então sinto muito, meu amigo, vá ler o Clean Code ou aprender a programar de forma limpa. Aliás, práticas de TDD ou BDD, por exemplo, influenciam e praticamente geram um código limpo e legível dadas as etapas que sugerem na construção da aplicação. Por exemplo, o famoso “Vermelho, verde, refatorar” do TDD – O refatorar ocasiona um cógido extremamente mais compreensivo. Construído tal código sólido e bem estruturado, eu questiono – comentários para que? Eles perdem o sentido. Por exemplo, você se depara com a seguinte expressão em Ruby:

unless valor == 0
    puts "Valor diferente de zero!"
end

Bem expressivo, certo? Pra que um comentário então? Uma das coisas que me irrita um pouco é encontrar códigos assim:

public class Pessoa {
    private String nome; //define o nome da pessoa
    private int idade; // define idade da pessoa
}

Ou pior, bem pior que isso:

public class Pessoa {
    private String a; //define o nome da pessoa
}

Ambos os casos eu considero inapropriados. O primeiro por gerer redundância – é ÓBVIO que o atributo “nome” faz menção ao nome da entidade pessoa! O segundo exemplo nos remete a alguém que não fez uso de bom senso de orientaćão a objetos para definir o nome do atributo. O comentário era necessário porque não houve refatoração e uso de um nom esclarecedor ao campo. De qualquer forma, isso é incorreto no meu ponto de vista.

Então quando usar comentários? Costumo usar comentários quando, por exemplo, utilizei algum recurso pouco conhecido da linguagem. Por exemplo, o uso de Fibers no Ruby – poucas pessoas sabem o que são Fibers; coloquei um comentário no meu código extremamente sucinto explicando o que era e onde achar mais informações. Ou se você utilizar alguma magia-negra no código, embebida por metaprogramação, por exemplo, acho que é bom deixar um comentário sucinto do que aquilo quis dizer. Esse tipo de comentário ajuda programadores juniors a adquirir experiência e ajuda até memso os seniors a captar a idéia geral de um método/bloco que possui lógica complexa.
E há alguns casos que comentários podem ajudar a leitura do código fluir e ficar mais agradável. Pra Java, por exemplo, existe uma API – GAG >= Google Annotations Gallery da qual já fui colaboradora. Essas anotações dão emoćão ao código; de vez em quando acho interessante utilziar algumas delas; tornam o coódigo mais expressivo. Novamente, acho que se deve usar sem abusos =)

Sobre o uso do @author – Aí já acho que é uma cosia meio pessoal. Eu não coloco. Saber que sou colaboradora/commiter do projeto já está bom pra mim. A ligação entre eu e o código fonte vai muito mais além que um @author. Claro, tem o lado bom de se colocar; todos que tiverem o fonte saberão o responsável por aquela ciração, e poderão parabenizá-lo, encontrá-lo no Twitter ou no Facebook e coisas do gênero. Ou poderão xingar sua mãe com vontade. Mas acredito que há outras formas de se descobrir e pedir ajuda sobre algum fonte que não são através do @author.

Bom, essa é minha visão geral da coisa! =D

E aí, é contra ou a favor do @author ?