Retrabalho Não Combina Com Equipes Ágeis

January 9, 2009

Retrabalho significa jogar tempo, dinheiro, etc. fora. Certamente, ninguém quer isso. Para tentar evitar esse retrabalho, normalmente a solução proposta envolve especificar “bem especificado”, planejar “bem planejado”, tudo com muita antecedência. É a velha muscle memory agindo. Sabemos que isso não funciona. Simplesmente porque é impossível prever o futuro.

A solução que eu acredito funcionar é aplicar as práticas de XP. E não basta aplicar uma ou outra, temos que aplicar todas. Juntas elas promovem uma sinergia que não existe quando são utilizadas de forma isolada.

Práticas como TDD, refactoring, integração contínua, programação em par, cliente presente, sentar-se junto, design incremental garantem uma base de código saudável, com design simples. Com isso o custo de mudança permanece constante ou mesmo pode cair com o tempo.

Kent Beck viu em muitos times XP que ao longo do tempo os sistemas desenvolvidos por esses times ficavam cada vez mais flexíveis e fáceis de mudar ao invés do que estamos mais acostumados a ver, que são aqueles sistemas legados cujo custo de mudança cresce exponencialmente com o tempo. Tal afirmativa pode parecer ir contra o senso comum mas faz todo o sentido quando temos as práticas do XP aplicadas corretamente.

O custo de um time XP competente é inegavelmente alto. Mas compensa no longo prazo por permitir que sistemas não precisem ser reescritos do zero a cada x anos. Além disso, outros motivos existem para se ter um time competente. Por exemplo, evitar o custo dos Net Negative Producing Programmers. Ou seja, na minha opinião vale a pena pagar pelos melhores.

Tenho vendido a idéia de que se uma equipe ágil está funcionando bem, nunca haverá retrabalho e sim, simplesmente, trabalho.


Mockando funções do PHP

July 2, 2008

O phpunit é um framework para testes unitários em PHP. Esse framework inclui funcionalidades para utilização de mocks. No entanto, temos uma limitação pois não é possível mockar funções do PHP. Nesse post vou mostrar um possível workaround para esta situação.

Vamos supor o código abaixo:

public function get($params) {

    // A partir dos $params passados, montamos uma $url

    return file_get_contents($url);
}

O código acima recebe um array de parâmetros. A partir dele, monta a url de um webservice REST. Por fim, faz um HTTP GET nessa url e retorna o xml recebido. A responsabilidade deste método é montar a url corretamente a partir dos parâmetros recebidos. Este é o comportamento que deve ser testado. O GET é feito pela função file_get_contents.

Idealmente queremos mockar a função file_get_contents. Mocks são usados com diversas finalidades. Neste exemplo, as duas principais motivações são: isolar o código de forma a só testar uma pequena unidade (montagem da url corretamente); e não fazer chamadas a um webservice real (por exemplo para não sobrecarregar um ambiente de produção ou porque o webservice não existe no ambiente de desenvolvimento.).

Já que não é possível mockar funções do PHP, como podemos mockar file_get_contents? Uma possível solução é criarmos uma classe cuja única responsabilidade é chamar esta função:

class FileGetContents {

    function file_get_contents($url) {
    	return file_get_contents($url);
    }

}

O código que queremos testar fica como abaixo. Note que uma instância de FileGetContents é injetada via construtor:

function ClienteHttp($fileGetContents) {
    $this->fileGetContents = $fileGetContents;
}

public function get($params) {
    // A partir dos $params passados, montamos uma $url

    return $this->fileGetContents->file_get_contents($url);
}

E podemos mockar a classe FileGetContents no nosso teste:

/**
 * @test
 */
public function deveria_chamar_webservice_com_url_correta() {
    $urlEsperada = "http://url.esperada";
    $fileGetContents = $this->getMock('FileGetContentsMock',array('file_get_contents'));
    $fileGetContents->expects($this->once())
    ->method('file_get_contents')->with($this->equalTo($urlEsperada))

    $clienteHttp = new ClienteHttp($fileGetContents);
    $parametros; // array de parâmetros que devem resultar na $urlEsperada
    $clienteHttp->get($parametros);
}

Alguns podem argumentar – corretamente – que a classe FileGetContents não é testada. Mas se levarmos em conta que ela simplesmente delega para uma outra função, a desvantagem não é tão significativa assim. Vale enfatizar a importância de que essa classe não tenha nenhum outro comportamento. Caso contrário, a falta de testes já não seria mais aceitável.