Quais tasks executar em seu servidor de CI ?

43328984

 

Ao implantar alguma forma de Integração Contínua no seu projeto, você certamente terá que escolher, dentre um oceano de  opções, um conjunto de steps que seu software deve percorrer até estar “pronto” para produção. Com base na minha tradução do excelente artigo sobre Integração Contínua do Martin Fowler  (que é um recordista de acessos do blog), decidi botar em pauta uma sugestão de pipeline, descrevendo seus steps.

A idéia deste artigo é ser agnóstico em relação ao seu CI server, mas como tenho um background maior com o Jenkins/Hudson, é possivel que use alguma terminologia desse servidor. Se tiver alguma dúvida, pergunte lá na parte de comentários que eu tento ajudar. Vamos lá!

Decidindo quem entra e quem sai

Screen Shot 2013-11-26 at 5.37.51 PM

Decidir quais passos são realmente relevantes para a publicação do seu software nem sempre é uma tarefa simples, dado que existem diversas técnicas e adotar todas elas transformará seu release em um  processo impraticável.

Eu tento seguir os seguintes steps nas pipelines que implemento:

  • Recuperação do código para a máquina de CI;
  • Atualização de bibliotecas locais na máquina de CI;
  • Execução de testes separados por tipos (rápidos primeiro, lentos depois);
  • Atualização da versão stable e geração de nova versão snapshot;
  • Empacotamento do software/Criação de Tags/Publicação para o servidor de artefatos;
  • Deploy para o ambiente de Homologação/Produção (be careful)

Óbvio que nem todos os steps acima são aplicáveis a todos os ambientes, mas servem de exemplo pra gente pegar a idéia. Cabe a você decidir o que colocar no seu projeto ou não. A idéia mínima é a seguinte: pegar o código, rodar os testes e passar o software adiante, quer seja publicando ou congelando em uma versão validada para futuro deploy.

A idéia por trás de cada Step acima é :

Recuperação do código para a máquina de CI

Esse passo é simples: você tem que pegar a última versão do seu projeto em desenvolvimento do seu controle de versão. Vários servidores de CI possuem algum tipo de plugin para comunicar com os SCM mais conhecidos (SVN, GIT, HG, CVS, etc) . A grande decisão nesse passo é se você vai optar por rodar esses passos automaticamente a cada commit feito pela sua equipe de desenvolvedores ou se a sua pipeline iniciará manualmente, com alguém clicando em “Run”.

Minha opnião é : faça automaticamente, a cada commit. Porém existem alguns “poréns”. O primeiro porém é :  como sua pipeline pode demorar para rodar completamente, é possivel que ocorra novos commits enquanto seu servidor de CI ainda esteja rodando a pipeline do commit anterior e isso pode gerar falsos erros devido a execução paralela da mesma pipeline. Para resolver isso, aconselho você configurar seu CI server para rodar com somente um step por vez (sem paralelismo)o que impossibilita que diversas pipelines rodem simultâneamente. O ponto negativo disso é que, em contra-partida, seu processo de release ficará mais lento.

O segundo porém é que você deve se precaver para não permitir  que cada commit, ao final da sua pipeline, vá parar diretamente em produção. Certamente você não vai querer isso, independente se  o commit quebra ou não sua build. Nesse ponto, muita gente critica erroneamente o Continuous Delivery, pensando que ele prega que todos os commits irão gerar novas versões do seu software , e consecutivamente, essas versões devem ir direto para produção, sem um “double check humano”. Não, Continuous Delivery não diz isso, ele diz que a última versão do seu software sempre esta preparada para ir para produção, o que é beeem diferente de estar em produção.

chaves

 

Uma solução para isso (que na verdade não é nenhuma novidade) é sempre ter um ambiente de Homologação (ou QA, ou aceitação, seja lá como você o chame)  e apontar seu servidor de Integração para fazer o deploy ao final da sua pipeline para este servidor. Assim, você garante que sempre terá um ambiente testado, rodando e pronto para um double check humano, que viabilizará o OK para subida em produção.

 

Atualização de bibliotecas locais na máquina de CI

Nesse passo temos a tarefa de atualizar seu servidor de CI com todas as versões de bibliotecas externas que seu software usa. Um grande alívio para nós, desenvolvedores foi alguém, em algum lugar, ter tido a incrível idéia de integrar um gerenciador de pacotes ao no seu processo de desenvolvimento.

Uma vez que seu projeto use um easy-install, pip, gem, maven ou qualquer outra forma de especificar as bibliotecas, versões e repositórios, cabe nesse step você utilizar o programa responsável para instalar localmente essas dependências. É bom manter esse step separado dos demais para facilmente identificar algum problema na instalação/atualização dessas bibliotecas.

 

Execução de testes por tipos (rápidos x lentos)

Nesse step (ou steps) é onde você realmente roda os testes na sua aplicação – eu escrevi “steps” por que acho aconselhável separar cada tipo de teste em um step próprio e organizar para que os testes mais rápidos rodem primeiro, pois se eles quebrarem, você será notificado mais rápidamente. Um tendência bem comum é que alguns tipos de testes demorem muito para rodar, principalmente com o projeto crescendo. Um exemplo disto são os testes funcionais, que exigem uma observação adicional.

Testes funcionais exigem que você tenha um ambiente “up and running” para que possam rodar. Como a idéia da pipeline é você só atualizar seu  servidor se o software estiver testado, fica claro que você precisa de um ambiente adicional para rodar seus testes funcionais. O que faço é:  depois que todos os outros testes tiverem rodado, faço um deploy da aplicação para um ambiente separado e assim rodo os testes funcionais contra esse ambiente. Para os que usam um SO unix-like e dependam de um navegador para rodar os testes funcionais (comum para quem usa Selenium por exemplo), você pode usar o XBFV e rodar browsers virtuais por ele.

 

Atualização da versão stable e geração de versão snapshot

n64-kid_o_GIFSoup.com_thumb

Parabéns campeão, seu presente tá quase pronto. 

Bom, se seu commit passou por todos os passos anteriores, meus parabéns, isso significa que seu código esta rodando e portanto, falta muito pouco para ir ao ar. E nesse passo você sinaliza isso, fazendo a atualização da versão que esta em pronta para deploy e da versão que esta em desenvolvimento. É comum, por exemplo, você adotar uma nomeclatura que diferencie a versão em desenvolvimento da versão que esta pronta pra deploy.

Por exemplo, vamos imaginar que temos as seguintes versões antes de rodar a pipeline:

  • Última versão “estável” : 1.01
  • Versão em desenvolvimento: 1.02-SNAPSHOT

Depois de rodar sua pipeline, você terá:

  • Última versão “estável” : 1.02
  • Versão em desenvolvimento: 1.03-SNAPSHOT

Uma regra bem definida na nomenclatura das versões do seu software ajuda a identificar o que esta pronto e o que esta em desenvolvimento, além de ser possível ter um roadmap de que alterações entraram em cada versão. Alguns sistemas de gerenciamento de tarefas (como Jira) podem até fazer a associação das tasks X versões do seu software automaticamente. Procure alguma biblioteca que te ajude a controlar as versões do seu sofware (em Ruby tem o versionomy), mas um simples controle com um arquivo .version na raiz do seu projeto já funciona.

 

Empacotamento do software/ Criação de Tags/ Publicação para o servidor de artefatos

Essa é a hora de comunicar ao mundo que seu software esta pronto para ir ao ar. Nesse step, você já pode “congelar” seu código publicando para os devidos repositórios e gerenciadores de artefatos.  Geralmente os SCMs tem alguma forma de sinalizar as versões fechadas (como as Tags do SVN). Já vi algumas pessoas criarem branches no Git/Hg com as versões geradas no passo anterior ou até mesmo publicar um .zip com a aplicação para algum repositório de artefatos. A idéia é você ter um lugar de rápido acesso caso precise pegar uma versão antiga para reverter um deploy catastrófico que foi feito no ambiente de homologação/produção.

Deploy para o ambiente de Homologação/Produção (be careful)

43327918

Por uma política interna muito sensata (mas burocrática), algumas empresas impedem que os processos de publicação a produção sejam de responsabilidade da equipe de desenvolvimento, delegando a uma equipe de SysOps essa responsabilidade. Se esse é o seu caso, uma simples notificação ao final da pipeline pode ser enviada ao setor responsável, suprimindo assim a necessidade de chamadas diretas a algum software de deploy.

Para ser franco, o processo de subida geralmente não é um processo simples, principalmente para ambientes distribuídos. Geralmente você tem diversas máquinas em nuvem que precisam serem atualizadas e não pode derrubar a aplicação completamente para upgrade, correndo o risco de apresentar alguma inconsistência em seu serviço. O Twitter, Facebook e outras empresas com um número gigante de servidores em produção precisam até usar métodos extremamente alternativos (como publicação de build via torrent) para acelerar suas subidas para produção em suas milhares de máquinas. Embora seja muito bacana, isso é papo para outro post.

Conclusão

Tentei nesse post ser breve, sem deixar de passar por alguns pontos que achei importante. É óbvio que muita coisa foi posta de fora, mas nada que novos posts ou um bate papo na parte de comentários abaixo não resolva. Pretendo, futuramente, fazer um step-by-step ensinando a como encadear os jobs no seu Jenkins, criando uma pipeline parecida com essa que descrevi. Aguardo seus comentários :-)

Code Smells – Duplicação de Código

 

Semana passada postei uma tradução do artigo em inglês sobre Code Smells, escrito pelo Martin Fowler. Pretendo criar alguns posts rápidos comentando sobre smells clássicos e possíveis formas de combatê-los. Por hoje, vamos ver em Ruby como combater duplicação de códigos usando um design pattern conhecido como Template Method.

 

Identificando o problema

 

Um cenário muito comum ao desenvolvermos software é nos depararmos com a seguinte situação: uma quantidade significativa de código que resolve um problema X, mas que precisa sofrer pequenas alterações para resolver também o problema Y, Z e “n” outros problemas muito parecidos entre si.

Vamos supor que você esteja trabalhando em um projeto que precisa gerar boletos bancários para diversos bancos. Ao olhar o código do sistema em produção, você percebe que  algum desenvolvedor no passado (que já saiu da empresa, afinal ninguém quer assumir a culpa) resolveu brilhantemente isolar o código para gerar boletos bancários para o Itaú em uma classe chamada BoletoItau - classe essa que parece com a seguinte:

 Parabéns para o desenvolvedor que resolveu isolar o código acima em uma classe. Mas, quando esse mesmo desenvolvedor teve que gerar um boleto para o banco Santander (que parece muito com o boleto do Itaú) ele fez algo muito, muito ruim: ele duplicou a classe BoletoItau, criando a BoletoSantander:

 

Agora você, que esta com a tarefa para criar boletos para o Banco do Brasil, se vê diante das opções: perpetuar esse legado de código duplicado, ou refatorar o código acima para algum design pattern existente. Não se deixe seduzir pelo lado negro da força, gaste um tempinho e resolva permanentemente esse problema com o Template Method.

tumblr_mp8dtqhOmI1sx2peuo1_500

 Darth Vader tá vendo você querendo manter a POG.

 

Identificando o que permanece igual

 

A chave central desse dilema é identificar o que se repete entre os códigos semelhantes e isolá-los em uma classe que servirá de base para todas as subclasses.  Uma definição formal do pattern Template seria:

A idéia geral do Pattern Template Method é criar uma classe base abstrata com um método que sirva de esqueleto. Esse método possui o processamento que pode variar, realizando chamadas a métodos abstratos, que serão fornecidos por subclasses concretas. (Design Patterns in Ruby)

No exemplo anterior, vemos que as principais diferenças entre as classes BoletoItau e BoletoSantander se encontra no método gerar_boleto. Para transformar o método gerar_boleto em um Template Method, eu preciso antes transformar o código que ele executa em chamadas as métodos que serão sobrescritos por cada subclasse, que no meu exemplo, será cada Banco.

Uma proposta da classe Boleto seria:

  Com isso, a cada novo Banco, eu posso simplesmente extender a classe base Boleto e sobrescrever os métodos desejados. Os bancos poderiam ficar da seguinte forma:

 

Conclusão

 

O exemplo foi bastante reduzido para não tornar este post em uma dissertação acadêmica, mas adianto que  existe um mar de técnicas que podem ser aplicadas para melhorar ainda mais esse exemplo.

Caso queira uma recomendação de livro, indico o Design Patterns – Elements of Reusable Object-Oriented Software, que é um livro mais abrangente sobre o tema. Caso queria algo mais prático, tem o excelente Refactoring to Patterns (Java) que é um livro mais prático sobre Patterns. Em Ruby, existe o Design Patterns in Ruby, que usei como base para este post – e possivelmente usarei como base para os próximos posts.

Code Smells

code_smellsO texto abaixo é a uma tradução do artigo escrito pelo Martin Fowler. Para acessar a versão original em inglês, clique aqui . Caso você tenha sugestões para tornar a tradução melhor, compartilhe através da seção de comentários no final da página.

 

Um code smell é uma indicação superficial que geralmente corresponde a problemas mais profundos no sistema. Esse termo foi primeiramente usado pelo Kent Beck quando ele me ajudou no meu livro Refactoring.

A definição acima de divide em dois pontos. Primeiro, um smell (do inglês “cheiro”) é por definição algo que é fácil identificar – ou fareijar como eu falei recentemente. Um método longo é um bom exemplo disso –  basta ver mais que uma dúzia de linhas de Java.

O segundo ponto é que smells nem sempre indicam um problema. Alguns métodos longos são simplesmente bons. Você tem que olhar mais profundamente para saber se existe um problema oculto – smells não são necessariamente ruins por si só – eles são frequentemente indicadores de um problema ao invés de serem por si só o problema.

Os smells mais comuns são algo que é fácil identificar e a maior parte do tempo nos leva a problemas interessantes. Classes de dados (classes que possuem somente dados e nenhum comportamento) são bons exemplos. Olhe para elas e pergunte a você mesmo qual comportamento deveria existir naquela classe. Então você começa a refatorar inserindo esse comportamento pra lá. Geralmente perguntas simples e refatoramentos iniciais podem ser um passo vital para transformar objetos anémicos em algo que realmente tenha importância.

Uma das boas coisas sobre smells é que eles são fáceis para pessoas inexperientes identifica-los, mesmo se eles não souberem avaliar se existe realmente um problema ou como corrigí-lo. Tenho ouvido alguns líderes de desenvolvedores que elegem o “smell da semana” e pedem as pessoas para procurar o smell e trazer aos desenvolvedores seniors da equipe. Fazer isso em um smell por vez é uma boa forma de gradualmente ensinar as pessoas do time a serem melhores programadores.

Testes funcionais com Cucumber e Page Objects

 

Aproveitando que meu último post foi justamente uma tradução do ótimo artigo do Martin Fowler sobre  Page Objects, vou engatar no tema e postar algumas sugestões de como usar esse pattern com o cucumber para criar os testes funcionais da sua aplicação. Caso queria uma introdução sobre Page Objects, recomendo a leitura do post anterior a este, clicando aqui.

Ingredientes

A idéia é construir o seguinte ambiente:

  1. Testes no cucumber  : consomem suas pages.
  2. Suas pages: definem seus page-objects, que encapsulam chamadas ao seu driver
  3. Watir-webdriver : Manipula chamadas ao Watir
  4. Watir : Manipula o browser, que no meu exemplo será uma instância do Firefox.

O mundo real e os testes funcionais

Testes funcionais são, por natureza, difíceis de manter. Isso  porque além deles serem sensíveis a mudanças do seu código server-side, eles são sensíveis a mudanças na interface da sua aplicação web (e portanto ao código client-side). Logo, por exemplo, se o programador front-end mudar o id de algum elemento importante para os seus testes, ou construir um código javascript que obstrua o carregamento da página (no caso de uma requisição ajax para montar um bloco HTML), seus testes facilmente irão falhar.

Além destes pontos, as interfaces web hoje em dia estão bem difíceis de serem testadas. Já eram na “Era Flash” e suas complicações com Selenium e embora as interfaces estejam cada vez mais javascript-based, você ainda tem complicações devido as diversas requisições ajax e “mágicas javascript”, que tendem a dar algumas dores de cabeça ao serem testadas por um driver web.

Proposta

Pretendo aqui montar um projeto exemplo de testes funcionais, testando uma busca no meu Blog. Eu aconselho você, no seu projeto real, manter os testes funcionais (com cucumber e page objects) em um projeto separado da aplicação a ser testada, ainda que esta seja em Ruby também. Isso é bom, principalmente por que você isola as dependências das bibliotecas peculiares a cada projeto (da aplicação a ser testada e dos testes).

Recentemente tive que criar testes funcionais em uma aplicação web Scala/Lift, e manter o projeto de functional tests separado foi fundamental para isolar as dependências J2EE/Scala do projeto de testes em Ruby/Cucumber – além de facilitar o isolamento dos jobs na pipeline do servidor de CI, que no meu caso foi o Jenkins. Caso alguém se manifeste a favor, posso postar futuramente como configurar um job no jenkins para testes funcionais em Ruby/Cucumber.

 

Passo 1 : Defina suas Páginas

Uma forma de começarmos a testar é definir antes suas pages e então definir seus objects. A gem page-object te permite fazer isso, usando uma DSL simples e intuitiva. Neste projeto exemplo, eu criei a seguinte Page:

Como você pode ver, eu defini os page  objects usando uma DSL que me remete aos próprios elementos HTMLs. Para cada page object, a lib ruby “page-object” cria diversos métodos “mágicos” que auxiliam nos testes. Alguns exemplos:

Existem diversos outros métodos, que você pode encontrar diretamente na documentação da gem.

Passo 2 : Defina suas Features

Essa parte é mais fácil, basta seguir o padrão já estabelecido pelo próprio Cucumber, sem grandes mudanças.

Minha única dica nesse ponto é adicionar um sufixo numérico no nome do arquivo físico da sua feature (ex. 001_minha_feature.rb). Frequentemente seus testes funcionais precisam seguir uma sequência pré-estabelecida  (e só encontrei essa forma no Cucumber para ele respeitar alguma ordem):

Exemplo:

  • 001_cadastrar_usuario.rb
  • 002_ativar_usuario_cadastrado.rb
  • 003_acessar_conta_como_usuario.rb

 

Passo 3 : Defina seus steps usando suas pages (e seus page-objects)

Usando as gems watir_webdriver e a page object, ganhamos alguns métodos importantes nos nossos steps. Um deles é o visit_page, que aponta o browser para a url definida na Page – geralmente usado no início dos testes.

Outro item importante: você pode ter várias pages com a mesma url, caso queria testar uma página que tenha comportamentos diferentes.

No projeto-exemplo, os steps para a HomePage ficaram:

Deixo duas dicas para esse passo: crie um arquivo (ou alguns) para passos que se repitam em diversas features. É comum, por exemplo, nos testes funcionais de uma aplicação que tenha algum tipo de permissionamento baseado em papéis (ACL), que você tenha que se autenticar na aplicação diversas vezes, com usuários diferentes. Para isso, coloque todos os steps necessários para fazer  o login em um arquivo só. Como os arquivos que definem os steps são “compartilhados” por todas as features, todos os steps são públicos a todas as features.

A segunda dica é : cuidado com os waitings explícitos. É comum, graças a problemas com timeouts, você colocar algum tipo de wait na página. Por exemplo:

Ou mesmo:

Geralmente, testes com Selenium possuiam muitos waitings explícitos – e isso era um grande causador de lentidão nos testes, que demoravam horas para rodarem. É óbvio que os waits explícitos às vezes são a única saída para lidar com algum timeout, mas evite sempre que possível.

 

Conclusão

Caso você baixe o projeto-exemplo, ao rodar no seu terminal você deverá ver algo parecido com isso:

Testes funcionais são custosos, mas são ótimos para capturar erros causados por algum problema na interface ou que não estejam necessáriamente ligados ao seu código (problemas de rede, por exemplo).

Por fim, deixo três conselhos para aqueles que tenham algum tipo de servidor de Continuous Integration ou mesmo Continuous Deployment:

  • Na sua pipeline de testes, rode os testes unitários antes dos testes funcionais.
  •  Rode os testes funcionais em uma aplicação “deployada” em um ambiente isolado do ambiente de produção, mas que seja o mais parecido possível com o mesmo.
  • Rode os testes funcionais em um ambiente gráfico virtual, tal como o XVFB

Deixei uma cópia do código usado nesse projeto no meu github : https://github.com/pedrolopesme/post-functional-tests .

Tradução do artigo sobre PageObjects

 

O texto abaixo é a uma tradução autorizada do artigo escrito pelo Martin Fowler. Para acessar a versão original em inglês, clique aqui . Caso você tenha sugestões para tornar a tradução melhor, compartilhe através da seção de comentários no final da página.

The text bellow is an authorized translation of the article written by Martin Fowler. To access the original english version, click here . If you have any suggestion to make the translation better, share it via comments section at the end of the page. 

 

Quando você escreve testes para uma página web, você precisa criar referências a elementos na página para clicar nos links e checar o que é exibido. Entretanto, se você escrever testes que manipulem os elementos HTML diretamente seus testes vão estar frágeis a mudanças na UI. Um page object abstrai uma página HTML (ou parte) em uma API específica da aplicação, permitindo você manipular os elementos de página sem se aprofundar no HTML.

 

pageObject

 

A regra básica para um page object é que ele deve permitir um software cliente fazer qualquer coisa e ver qualquer coisa que um humano pode. Ele deve também prover uma interface que seja fácil para programar e esconda as camadas internas da janela. Com isso, para acessar uma caixa de texto você deve ter métodos de acesso que recebam ou retornem uma string, check boxes que permitam usar booleans e botões que devem ser representados por métodos que correspondam a sua ação . O page object deve encapsular os mecânismos necessários para encontrar e manipular os dados no controle da interface gráfica. Uma boa regra é imaginar mudando o elemento de fato – no caso a interface do page object não deve mudar.

Indiferente do termo “page” object, esses objetos não devem geralmente ser construidos para cada página, mas ao invés disso, para elementos relevantes na página. Uma página que exibe vários álbuns, por exemplo, deve ter um page object de uma lista de álbuns, contendo vários page objects de álbuns. Eles deve possivelmente também ter um page object de cabeçalho e um page object de rodapé. Com isso, parte da hierarquia de uma UI complexa existe somente para estruturar a UI – tal composição de estruturas não devem serem reveladas pelos page objects. A idéia é modelar a estrutura da página de modo que faça sentido para o usuário da aplicação.

Da mesma forma, se você navegar para outra página, o page object inicial deve retornar outro page object para a nova página. No geral, operações de page objects devem retornar tipos fundamentais (strings, datas) ou outros page objects.

Existem diferenças de opinião se page objects devem incluir assertions em si, ou simplesmente prover dados para que os scripts de teste façam os assertions. Adeptos por incluir os assertions nos page objects dizem que isso ajuda a evitar duplicação de assertions nos scripts de testes, facilitando a produção de melhores mensagens de erro e suportando mais uma API no estilo TellDonAsk*. Adeptos de page objects sem assertions dizem que a inclusão de assertions misturam as responsabilidades de prover acesso aos dados da página com a lógica de assertions, e levam a um page object inchado.

Eu sou a favor de não criarmos assertions nos page objects. Eu acho que você pode evitar a duplicação criando bibliotecas de assertions para os assertions comuns – o qual pode também facilitar a provisão de diagnósticos.

Page objects são geralmente usados para testes, mas não devem fazer assertions eles mesmos. Sua responsabilidade é providenciar acesso ao estado das partes internas da página. É responsabilidade dos clientes levar a lógica dos assertions.

Eu descrevi esse padrão usando como base o HTML, mas o mesmo padrão pode ser aplicado em qualquer tecnologia de UI. Eu tenho visto esse padrão sendo usado efetivamente para ocultar detalhes de UI em Java Swing e não tenho dúvidas que isso é amplamente usado em quase todas frameworks de UI existentes.

Problemas de concorrência são outro tópico que um page object pode encapsular. Esses casos podem ter que ocultar a assíncronia em operações assíncronas que não podem serem exibidas ao usuário como assíncronas. Page objects podem encapsular problemas de threads em frameworks UI onde você tem que se preocupar em um comportamento entre UI e threads em funcionamento.

Page objects são geralmente usados para testar, mas também pode ser usados para providenciar uma interface de scripts sobre uma aplicação. Geralmente é bom ter uma interface script por debaixo da UI – o que é mais rápido e mais seguro. Entretanto, com uma aplicação que tem muitos comportamentos na UI, usar page objects pode ser a melhor ajuda em um trabalho ruim – mas tente mover a lógica se possível, será melhor tanto para os scripts quanto para uma UI saudável por mais tempo.

É comum escrever testes usando alguma Linguagem Específica de Domínio (DSL), tal como Cucumber ou outra DSL interna. Se for o seu caso, é melhor arranjar sua DSL de testes para usar os page objects, permitindo você ter um parser que traduza suas chamadas a DSL em chamadas aos page objects.

Caso você tenha chamadas a um WebDriver em seus testes, você esta fazendo isso errado – Simon Stwart.

Padrões que digam para mover a lógica para fora dos elementos UI (tal como Presentation Mode, Supervising Controller ou Passive View) tornam menos úteis os testes na UI e assim, reduzem a necessidade dos page objects.

Page objects são um exemplo clássico de encapsulamento – eles ocultam detalhes da estrutura do UI e a componentização de outros componentes (e testes). É um bom princípio de design procurar situações como essa enquanto você desenvolve – perguntando a você mesmo “como eu posso ocultar alguns detalhes do resto do software?”. Como em qualquer encapsulamento, isto envolve dois benefícios. Eu tenho ressaltado que você deve colocar a lógica que manipula a UI em um único lugar onde você pode mudar sem afetar outras partes do sistema. Um benefício consequente disso é que isso permite ao cliente testar um código mais fácil de ser entendido, uma vez que a lógica ali é sobre a intenção do que se ter testar, não relacionando a detalhes de UI.

Resenha de Livro : APIs – A Strategy Guide

20111231-1110

button_comprar

Acabei de entrar na reta final do livro APIs: A Strategy Guide  e acho que já vale me adiantar e publicar uma resenha rápida do mesmo. O livro em questão é do Daniel Jacobson (@daniel_jacobson), juntamente com Greg Brail e Dan Woods. Estes dois últimos eu nunca tinha ouvido falar, mas o Daniel Jacobson é simplesmente um dos diretores de Engenharia da Netflix, responsável justamente pela equipe que toca a API da empresa.

Comprei esse livro com grandes expectativas. Minha intenção era encontrar um conteúdo que trouxesse a tona um debate sobre API Design, explorando os padrões já utilizados por grandes aplicações e seus prós e contras – mas infelizmente esse desejo não foi realizado. Não me entenda mal: o livro não é ruim, mas não foi tão proveitoso pra mim.

Logo de início o autor gasta um tempo falando sobre o valor de uma boa API, que toda aplicação deveria começar por ela e como grandes empresas atuais só foram possíveis por conta de uma boa API pública/privada. E assim segue o livro, explicando sobre algumas definições genéricas sobre APIs e alguns casos de uso. Mas nada de API Designs e virtudes /consequências sobre cada pattern. Roubando um trecho de um comentário na Amazon sobre esse livro, a frase que resume o mesmo é: “Você deve usar uma API. E neste livro você vai encontrar as razões para isso”.

Minha nota para o livro:  2.0  (de 5)

 

 

Scala : Curso gratuito com guru da linguagem

 

Scala_logo

 

Muito tem se falado sobre o poder das linguagens funcionais atualmente – o velho entrou (novamente) na moda. Fiz há alguns meses este curso do Coursera, que é ministrado pelo próprio Martin Odersky – pai da linguagem e quase uma lenda viva da programação. O curso é realmente excelente, cobrindo diversos aspectos básicos da linguagem e possuindo uma série de exercícios bem bacanas de programação.

O curso compreende:

  1. Week : Programming paradigms; overview of functional programming and the Scala programming language.
  2. Week : Defining and using functions, recursion and non-termination, working with functions as values, reasoning by reduction.
  3. Week : Defining and using immutable objects, review of inheritance and dynamic binding.
  4. Week : Types and Pattern Matching
  5. Week : Working with Lists
  6. Week : Collections and Combinatorial Search
  7. Week : Lazy Evaluation

O curso já abriu, mais ainda dá pra se cadastrar. O link é https://www.coursera.org/course/progfun.

Pé na estrada com phonegap: primeiros passos

 

Visto a complexidade que é desenvolver  aplicações para as diversas plataformas mobile existentes, vários projetos tem aparecido propondo soluções  na construção de aplicações hibridas. O caminho é quase sempre o mesmo: construir aplicativos com interface HTML 5/CSS 3, usando alguma linguagem de programação que seja o hype do momento, aka. javascript, python ou ruby.

O phonegap é mais uma dessas opções para construção de apps híbridas, fazendo frente a plataforma mais famosa nesse ramo, o Titanium. Não pretendo fazer um comparativo entre as duas soluções – até por que já fizeram a dar com balde ( aqui ). Quero compartilhar com vocês algumas idéias e a solução de alguns problemas que tive nesse começo de projeto.

 

Jogo rápido: Como realmente funciona o phonegap

Não tem muito mistério, ele pega toda a cancalharia client-side de uma app web, junta tudo e coloca numa WebView dentro da sua app  - o que acaba sendo um pouco diferente do Titanium que gera código nativo.  Os desenvolvedores do phonegap se preocuparam em oferecer configurações que permitem deixar sua app se comportando exatamente como uma app se comportaria e ainda forneceram APIs via JS para controlar recursos mais complexos dos SDKs, como interação com alguns recursos de hardware por exemplo.

Embora a lista de plataformas suportadas seja bem grande, é  óbvio que esse process  tem limitações se comparado a construir uma app em um SDK nativo, mas como nada nesse mundo funciona pra tudo, é questão de botar na balança e pesar os ônus e bônus da idéia.

 

1) Qual a parafernalha usar.

O phonegap deixa a gente bem a vontade para usar o que quisermos se tratando de ferramentas utilizadas na parte client side do desenvolvimento web. Escolhemos aqui na equipe adicionar o seguinte ao ciclo de desenvolvimento do projeto:

  • LESS CSS para as folhas de estilos, usando SimpLESS para compilação contínua.
  • Jasmine para testes JS (inclusive o phonegap já vem com algumas specs)
  • Jasmine-Jquery integração Jasmine-Jquery e suporte a fixtures em HTML
  • Jasmine-Ajax mock na camada de ajax para predefinir resposta a serviços externos.
  • Jquery Mobile, que dispensa apresentações.

 

2) Javascript

 

Logo de início o phonegap apresenta algo bem próximo da árvore de arquivos apresentada acima. Tudo que vamos criar basicamente fica dentro do diretório www. Faz bem você definir como irá trabalhar para não ter dezenas de arquivos se chamando, sem nenhuma organização. Existem até algumas frameworks que te fornece uma estrutura MVC em JS, tal como o ember.js,  angular.js ou o famoso backbone.js. Certamente no próximo projeto usarei algum deles.

Uma particularidade pra quem pretende usar o jQuery Mobile, é que ele faz uma navegação através de suas transições, sempre incluindo a nova página dentro da inicial. Para uma app web comum isso é loucura, mas para uma app mobile isso é fundamental. Como o cenário de uma app mobile é infinitamente mais controlado que na web, o caminho comum é usar o index.html como um loader de todos os resources que você precisa, e imediatamente após carregar tudo despachar a app para a primeira view da aplicação. O motivo disso é simples:  O jQuery Mobile não executa nenhum JS vindo de arquivos que não sejam o inicial.

Outro ponto com relação ao jQuery Mobile é quanto a sua performance: ele é meio lento. Testando no iPad 1, a app demora um pouco para responder as interações (sombras, highlights de seleção, etc). No iPad 2 fica perfeito. Já ouvi boatos que em alguns celulares a app pode ficar impraticável. Na dúvida faça você mesmo alguns testes, e caso não goste do resultado, mude para algo como o Sencha Touch.

 

3) Configurações do projeto

O phonegap vem por padrão com algumas configurações que você queira mudar:

  1. Ele não permite que seu código faça chamadas para endereços fora da aplicação, o que impede de você consumir APIs externas, por exemplo. Mude isso no arquivo cordoba.plist, adicionando ao ExternalHosts o endereço que você precisa acessar (ou um * para liberar qualquer endereço)
  2. Ele não bloqueia o “Bounce”da app, permitindo o toque na tela “jogar” a app de um lado para o outro. Fica extranho e não é comum em uma app nativa. Desligue esse comportamento alterando o UIWebViewBounce para false, no arquivo cordoba.plist.
  3. Os botões do jQuery Mobile, ao serem pressionados exibem a caixa de diálogo para Copy and Paste, o que não faz sentido algum. Para desligar isso foi necessario o seguinte trick sujo via CSS:* { -webkit-touch-callout: none; -webkit-user-select: none; }
    input { -webkit-user-select: auto; }

 

4 ) Testanto localmente no browser

Se a aplicação é puro JS/HTML/CSS, é óbvio que você vai querer ficar testando no browser local da máquina que esta desenvolvendo em vez de ficar fazendo build pro device. A única recomendação ao fazer isso, é acessar os htmls via um servidor web local, em vez de usar o file://. O python mesmo vem com o SimpleHTTPServer, é só navegar até o diretório www e digitar:

python -m SimpleHTTPServer

Depois acesse o http://localhost:8000 e  comece a desenvolver.

Instalando wkhtmltopdf em uma Amazon EC2

 

Depois de uma bela surra de gato morto, é bem fácil instalar o wkhtmltopdf em uma instância Amazon Ec2 AMI baseada em CentOS. Para quem não conhece o wkhtmltopdf, é uma app usada pra gerar PDFs  de páginas HTMLs, usando Webkit como motor de renderização. Esta app possui bindings pra php, python (e django) e ruby (via PDFKit),  facilitando um pouco a vida de quem precisa gerar PDFs. Não é uma maravilha, mas funciona.

Continue reading