Rua sem saída: não consigo criar meu próximo teste – Parte II

Continuando o assunto como criar meu próximo teste, irei abordar os três tópicos seguintes do segundo post da série. Recomendo que os leiam em ordem:Continuando o assunto como criar meu próximo teste, irei abordar os três tópicos seguintes do segundo post da série. Recomendo que os leiam em ordem:
* Prelúdio* Parte 1 – Tópicos 1, 2 e 3

Estou querendo testar um método privado

Quem nunca não é mesmo? Tudo pode começar com uma inocente vontade de verificar apenas um método privado em apenas uma classe do teu software. Após pesquisar, você concluí: ah, se a linguagem de programação X permite que eu modifique a visibilidade do método para public via reflexão (parte da metaprogramação) é porque isso é bom. Será ?
O primeiro ponto a se analisar é relembrar o que não é Test-Driven Developmentspoiler: não é sobre testar se o software não tem bugs. Com a mente fresca, pode-se perceber que se TDD é uma prática de design de código que tem por objetivo guiar seu design baseando-se nos requisitos funcionais do software, por si só, TDD é contra teste de método privado.
Definição método privado por Sandi Metz: métodos privados são métodos unstable da sua API. Entende-se aqui por API todo e qualquer método público de quaisquer objetos. Os métodos privados contêm o necessário para auxiliar métodos de negócio a realizarem suas atividades e por si só, não desempenham quaisquer atividades de valor ao software.
Então porque quero testar meu método privado? Por causa de uma má definição do design da classe ou feature que você está implementando. A dica chave aqui é repensar nas suas atribuições, ou seja, será que esse método na verdade não deveria ser uma nova classe? Extração em classe aqui geralmente ocorre, então se está travado nisso, pare e repense o design do código que entorna isso.
Quer ver como isso acontece e você nem percebe? Assista essa apresentação de Michael Feathers:

Não consigo isolar a feature que vou criar. Os objetos que irão interagir com ela são complexos para mockar

Situação básica onde você se empolga codificando e quando vai ver virou Big Ball of Mud. Pode ser também que você ficou no deixa disso e não fez Test-First.
Nota para os deixa disso: não estou afirmando que Test-First é a salvação do mundo e que a paz será atingida quando todos o fizerem – meu alerta sobre Test-First é aquele aviso que seu pai/mãe te deu quando você tentou apertar um parafuso do tipo philips com uma chave de fenda, e ignorando-o(a) você seguiu. Foi doloroso, demorado e ficou mal apertado, não foi?
Geralmente, você faz mocks utilizando doubles ao invés de instanciar objetos reais para interagir com seu objeto alvo do teste. Se no teste você precisa que seu double chame um método que chame outro para no fim retornar algo, você simplesmente pode fazer seu double retornar o valor esperado do método que seu objeto em teste precisa e pronto, correto?
O problema escala quando você tem objetos métodos que não podem ser mockados. Por que isso acontece? Bem, quando você não injeta suas dependências. Veja só:

class DeixaDisso
def tdd_eh_mimimi(params)
Paypal::RestAPI.perform("/paymenow", params)
end
end

Como é que você testa o DeixaDisso#tdd_eh_mimimi se ao testar isso ele chamará de verdade o Paypal::RestAPI.perform?
Supondo que o código acima seja uma abstração aceitável, o ideal a fazer é extrair o Paypal::RestAPI e torná-lo injetável via método, por exemplo:

class DeixaDisso
def tdd_eh_mimimi(params, paypal_api: Paypal::RestAPI)
paypal_api.perform("/paymenow", params)
end
end

Com isso, no teste você pode passar um double para que responderá ao método perform e você pode fazer o mock dele criando a seguinte expectativa: __espero que seja chamado o método perform com os parametros: /paymenow e params uma vez.
Pronto. Isso é um teste de unidade baseado em expectativa muito útil e simples quando as dependências são injetáveis.
Houve uma vez que eu trabalhei com um framework PHP chamado Phalcon Framework. Quando fui fazer uns testes que envolvia Controllers, etc., descobri que ele disponibilizava os “Serviços” via di que na verdade era um container de injeção de dependência. Para chamar o serviço ‘paypal’ por exemplo, eu poderia fazer assim:

class FoobarController {
public function indexAction() {
$this->di->getService('paypal') # Objeto Paypal
}
}

Do ponto de vista do Mr. M isso é fantástico, pois você magicamente tem acesso ao $this->di que te lista todos os serviços registrados. Mas… como testar isto sem cair na real implementação dos serviços? A mesma ideia funcionava para os parametros de POST/PUT. Vinha magicamente e não injetado. Essa filosofia seguia também para os Models e ORM.

Não consigo verificar se um evento/callback do framework que estou usando foi chamado

Ora, como teste de unidade testa a unidade de uma funcionabilidade do software, deixar que o callback/evento seja chamado tornaria o teste um teste de integração, concorda?
Buscando evitar que este callback seja chamado de verdade no teste, a melhor solução é verificar se ele foi chamado dado uma condição. Em miúdos, criar uma expectativa em um mock. Como callbacks diferentes podem e são implementados de formas diferentes, não há um recipe mágico aqui, apenas a dica de que devemos garantir que ele é invocado utilizando da técnica de setar a expectativa no mock (como no item acima eu mencionei). Alguns frameworks fornecem assertions expecificos para você testar a chamada a callbaks. Por exemplo, o Rails te permite testar se um e-mail foi enviado sem enviá-lo de facto. Isso permite que você crie testes isolados para estas condições de negócio sem seguir para os mocks.
Por fim e não menos importante, você pode criar um spy no teste que te ajudará a verificar se o estado de um objeto muda após a chamada do callback. O RSpec tem assertion baseado em evento/mudança.

expect {subject}.to change {subject.will_change_x_to_y}.from(x).to(y)

To be continued.

Continuação em: Parte 3 – Tópicos 7, 8 e 9

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