Arduino Day

O site anda bem parado, estou sem tempo de criar material. Mas tenho novidades: com a ajuda de alguns amigos, criamos o Arduino-SC e no dia 12 de Maio, teremos nosso primeiro evento, o Arduino Day!

O grupo mal começou e já conseguimos vários apoios importantes, então se você me conhece ou e quer ajudar a realizar este evento, não deixe de me procurar.

Update

O evento foi irado! Alguns poucos contratempos, mas o apoio da galera foi sensacional e tudo ocorreu bem. Conseguimos sortear muitos brindes, trocar muitas idéias - levei minhas tranqueiras pra mostrar pra galera e tudo mais - e conhecer muita gente legal.

As fotos do evento estão na página no facebook e no site.


Iniciando meu primeiro e-Book

É isso mesmo que você leu, estou iniciando meu primeiro e-book!

O título do e-book é CI - Integração Contínua Sem Desculpa e já está disponível pra leitura - e palpites.

Bom, na verdade, por enquanto é um compilado de todos os textos que escrevi sobre CI, utilizando uma ferramenta que fui apresentado recentemente, o Gitbook, que achei tão sensacional que resolvi mover os textos do blog para esta ferramenta.

Inclusive, você já deve ter notado que o link para os posts sobre CI estão redirecionamento para o capítulo correspondente.

O livro é totalmente aberto a sugestões e está disponível no meu Github.

Espero que gostem :)


Vamos falar de refatoração

Ou porquê você deveria fazer seu trabalho direito

Tem muito tempo que o que mais ouço são “programadores” reclamando que tal biblioteca não faz, tal componente é bugado demais, framework x é mal feito por causa disso e daquilo… sempre que me pego no meio de um papo desses, lanço duas frases:

  • Muito mimimi e pouco pull request.
  • Porque você não arruma o código que te incomoda?

Porque programadores habilidosos, simplesmente não se mexem e arrumam o que está com problema ao invés de apenas reclamar ou citar mais problemas?

A resposta básica é de que o código legado não tem testes, não foi projetado pensando nisso e não tem como colocar. Pra mim isso é balela, sempre tem!

Muitos times também largam que “nós vamos rescrever a aplicação, na próxima versão vamos fazer direitinho seguindo as melhores práticas”, admita, isso nunca vai acontecer. Simplesmente porque você sempre vai pegar o máximo de tempo disponível simplesmente para arrumar os bugs da versão atual ou adicionar novas funcionalidades - sem testes, copiando e colando as mesmas gambiarras que “já funcionam” em outras partes da aplicação. Pode ser que isso até aconteça, então, o time se divide em dois grupos: o que vai manter a versão nova e o que irá prestar manutenção na antiga até que a nova seja lançada. O resultado disso é: a versão antiga, começa a ficar cada vez maior, consequentemente, com mais problemas, e o time que a está mantendo, cada vez mais desanimado e extressado, seja pelo excesso de problemas que acontecem ao mexer em determinadas partes do software, cujos efeitos colaterais são totalmente inesperados, quanto pela vontade de estar participando na nova versão do projeto, que parece ser “bem mais legal e menos extressante”. Neste tumulto, rolam muitas trocas de membros entre os times, para resolver inúmeras urgências e acabamos com dois projetos atrasados e um time completamente desmotivado.

Existe também um outro cenário, o da refatoração de código, seja ela para seguir num novo padrão ultra bacana que o time viu na internet e que funciona com empresa tal. Mas lembre-se: Refatoração sem teste é apenas manutenção - de risco - de código legado.

Então, antes de tudo, tente ao máximo fugir desses dois cenários apresentados, sua aplicação - e sua sanidade - agradecem.

Muitos pensam que adicionar testes em uma aplicação, é do dia para a noite, criar testes de integração, unitários, comportamentais, etc e logo terá 100% de cobertura do código; mas vamos com calma pequeno gafanhoto…

Começar a cobrir uma aplicação com testes deve ser feito com calma, paciência e principalmente, por partes. Por exemplo: vai refatorar um componente ou uma tela? Porque você não apenas escreve um teste de comportamento para aquela tela? Vamos imaginar que esta tela seja um formulário, com dois campos: nome e email, e dois botões, um de submit, e outro de reset. Nosso cenário de teste é simples:

  • acessar a rota do formulário e verificar se o mesmo existe
  • preencher os campos com dados inválidos e verificar se ao clicar em submit, o mesmo terá sua validação recusada
  • preencher os campos novamente, desta vez, com dados válidos, clicar em submit e verificar se a mensagem de sucesso é exibida
  • preencher o formulário, clicar em reset e verificar se os campos foram limpos.

Simples não? Em poucos minutos, você escreveria este cenário (protractor, selenium, webtest,cucumber, tanto faz) e garantirá que a tela está funcionando. Testes unitários, mocks, stubs? Esqueça isso, em outro momento, quando você for mexer em algum componente, que isso possa ser encaixado, você o faz. Sobrou tempo? Porque não escrever um teste unitário para este controlador então? E assim vamos, a cada interação um teste.

Vamos imaginar outro cenário, uma aplicação simples, MVC, onde você descobriu um bug que precisa ser corrigido urgentemente, após uma sessão de debug, você descobriu que o problema está em um dos models utilizados. Agora temos o ambiente perfeito para nosso teste unitário. Porque não, ao corrigir o bug, você não escreve um teste para este model e cobre o bug que foi corrigido? Aplicando um cenário de aceitação e outro de erro, para garantir o comportamento do mesmo.

Como podemos ver, iniciar testes em uma aplicação existente não é impossível e nem um bicho de sete cabeças, requer apenas paciência e uma dose de bom senso sobre o que testar em cada momento, você vai ver que com o passar do tempo, sua aplicação terá cada vez mais cenários e uma cobertura de testes crescente, onde a equipe se tornará cada vez mais confiante em manter aquele “código legado” onde todos antes tinham medo de pôr as mãos.

Para finalizar, deixo minhas dicas sobre como começar a cobrir sua aplicação com testes e a implementar um bom fluxo de trabalho:

  • comece com cenários simples, como apenas a verificação de determinado elemento na tela ao acessar uma rota, isso já garantirá que a tela está sendo corretamente (ou o mais próximo disso) renderizada
  • não tente efetuar todos os tipos de testes de uma vez, vá com calma, não tente abraçar o mundo
  • refatoração sem teste não existe
  • desde o primeiro momento, configure algum serviço de Integração Contínua, mesmo que seja apenas para rodar testes de cada commit
  • quando o CI estiver com um fluxo legal, configure também uma Entrega Contínua

Caso você se interesse em implementar um sistema de integração contínua, no blog existem alguns textos que podem te ajudar no processo, ou fale diretamente comigo que terei prazer em ajudar.

Até a próxima ;)


Formulário de Contato com SimpleForm

Com a onda de sites estáticos, uma das coisas mais chatas, é ver aquele famoso “formmail.php” no meio do projeto, ou mesmo, aqueles devs que gostam de fazer um código macarrônico, apenas para usar a função mail do PHP ou algo similar.

Também não sou o maior fã do mundo, de usar um (micro) framework apenas porque o site precisará ter um formulário de contato. Ajax? .. bom, vamos deixar pra lá e ir direto ao assunto…

Gosto de integrar serviços, e isso nós temos muitos na internet, inclusive, para tratar nosso famoso formulário de e-mail, a bola da vez é o SimpleForm. Que nos permite apenas criar nosso formulário, e apenas apontar o action para o a url com o token fornecido pelo site.

O conteúdo do e-mail pode ser totalmente personalizado no site, com um editor bem simples.

O bacana do serviço, é que é facilmente integrado ao Akismet para prevenir que seus e-mails sejam tratados como SPAM.

Agora ficou fácil criar o formulário de e-mail do seu site não é mesmo? Pode apagar o formmail.??? que você tem aí ;)


Utilizando Listeners no PHPUnit

O PHPUnit com seu arquivo de configuração phpunit.xml nos permite inserir um arquivo de bootstrap para ser incluído antes da nossa suíte de testes rodar, porém, em algumas ocasiões, não necessitamos desse bootstrap ou mesmo queremos executar um ação adicional apenas para determinada suíte.

Em um projeto que mantenho, eu queria rodar as fixtures apenas para uma suíte em especial, para as outras, era pura perda de tempo ficar aguardando este processo.

Antes, eu utilizava uma tarefa no Phing, que dependia de sub tarefas e etc, mas era um processo que tomava alguns segundos preciosos, e para fugir disso, minha alternativa era chamar outra tarefa que era praticamente uma cópia da primeira, sem as fixtures, sujando o código com duplicidades.

O PHPUnit trabalha com o Listeners, com isso, fica fácil escrevermos classes para atender cada necessidade, e mantemos o código extremamente limpo e organizado.

Então, vamos direto ao que interessa, adicionando nosso Listener no arquivo de configuração dos testes de nosso projeto phpunit.xml, utilizando a tag listener e informando nossa classe, nesse caso, iremos criar o listener Bootstrap no namespace Application, que é onde rodam os testes de integração e eu necessito das fixtures:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.3/phpunit.xsd"
         colors="true"
         backupGlobals="false"
         backupStaticAttributes="false"
         verbose="true">
    <php>
        <const name="APPLICATION_ENV" value="testing"/>
    </php>

    <testsuites>
        <testsuite name="Application">
            <directory>./tests/Application</directory>
        </testsuite>

        <testsuite name="Domain">
            <directory>./tests/Domain</directory>
        </testsuite>
    </testsuites>

    <filter>
        <blacklist>
            <directory suffix=".php">./vendor</directory>
        </blacklist>
    </filter>

    <listeners>
        <listener class="Application\Tests\Bootstrap" file="tests/Application/Tests/Bootstrap.php">
        </listener>
    </listeners>
</phpunit>

Depois de informarmos o PHPUnit que temos um novo listener para as suítes, basta escrevermos o mesmo, informando, a que passo vamos chamar, no meu caso, preferi rodar as fixtures, ao início da suíte da teste, nesse caso, sobrescrevo o método startTestSuite:

Você pode rodar seu listener no momento em que quiser, alguns exemplos de métodos a sobrescrever:

  • Antes de cada teste: startTest
  • Depois de cada teste: endTest
  • Antes da suíte de testes: startTestSuite
  • Depois da suíte de testes: endTestSuite
<?php
namespace Application\Tests;

use PHPUnit_Framework_BaseTestListener;
use PHPUnit_Framework_TestSuite;

class Bootstrap extends PHPUnit_Framework_BaseTestListener
{
    /**
     * @param PHPUnit_Framework_TestSuite $suite
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        if (strpos($suite->getName(), "Application") !== false ) {
            require_once 'phpunit-bootstrap.php';
        }
    }
}

Foi importante utilizar o require_once, para que o bootstrap rodasse apenas uma única vez, que era o que eu queria.

Abaixo, o phpunit-bootstrap.php, que chama meu bootstrap da aplicação e carrega as fixtures para a suíte:

<?php
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\Tools\SchemaTool;

$app        = require __DIR__ . DIRECTORY_SEPARATOR . 'bootstrap.php';

$metadata   = $app['orm.em']->getMetadataFactory()->getAllMetadata();

$tool       = new SchemaTool($app['orm.em']);

fputs(STDOUT, 'Dropping schema....');

$tool->dropSchema($metadata);

fputs(STDOUT, 'OK!' . PHP_EOL);
fputs(STDOUT, 'Creating schema....');

$tool->createSchema($metadata);

fputs(STDOUT, 'OK!' . PHP_EOL);
fputs(STDOUT, 'Loading fixtures...');

$loader   = new Loader();
$loader->loadFromDirectory('tests/Application/Fixtures');

$executor = new ORMExecutor($app['orm.em'], new ORMPurger());
$executor->execute($loader->getFixtures());

fputs(STDOUT, 'OK!' . PHP_EOL);

return $app;

E pronto, agora, basta rodar os testes normalmente, e as fixtures são utilizadas somente no módulo que preciso.

Mais informações: