segunda-feira, 10 de dezembro de 2012

Selenium Tutorial – Part II

Partindo do princípio que que a instalação dos recursos necessários correu bem, vamos começar a desenvolver os testes em Ruby usando o Selenium. Para consultar os detalhes sobre como preparar um ambiente para testar consultar o post anterior aqui.

Operações mais comuns com o selenium

Efetuar a inicialização do driver e navegação para uma página web deve ser efectuado usando o seguinte pedaço de código. A linha um têm como objectivo efetuar a inicialização do webdriver para utilização do Firefox, enquanto por outro lado a linha dois indica-nos para acedermos à página inicial do google.

  1:       driver = Selenium::WebDriver.for :firefox
  2:       driver.get "http://google.com"

Para efetuar a localização os elementos gráficos existem várias opções. Podemos identificar os mesmos por


ID – Podemos usar a localização por ID quando temos uma estrutura html em que o elemento procurado seja semelhante ao exemplo mostrado abaixo.



   1: <button id="buttonid">...</button>

Aqui usaríamos o método find_element de modo a encontrar o elemento procurado do seguinte modo

  1: element = driver.find_element(:id, "buttonid")

A partir daqui o elemento web fica guardado na variável element, para efetuarmos o tipo de operações que desejarmos. Estas operações podem ser clicks, asserts, entre outras. Do mesmo modo estão ainda disponíveis os seguintes modos de localização Classname, Tagname, Name, Link Text ou Partial Link Text, Css e Xpath. As duas últimas referências possuem uma sintaxe própria e mais detalhes sobre essa sintaxe podem ser encontrados aqui para o caso do XPath e aqui para o caso dos CSS selectors. O modo mais rápido de identificar estes valores é usando o utilitário FirePath que nos devolve estas duas expressões de forma automática.


De seguida apresentam-se alguns exemplos de como identificar e efetuar operações sobre as:


Escrever Texto em Caixas de Texto

Quando na página em questão temos um excerto de html, semelhante a que se apresenta em baixo

   1: <form>
   2: First name: <input type="text" name="firstname"><br>
   3: </form>
que tipicamente resulta em algo semelhante
First name:

o que queremos tipicamente fazer é escrever texto na caixa que nos é apresentada. Podemos fazer isto do seguinte modo.

  1: element = driver.find_element(:name, "firstname")
  2: element.set 'Joe'

 


Outra situação bastante comum é clicar em Botões, quando encontramos o seguinte elemento.



   1: <button id="mybutton">Click Me!</button>

o que resulta no seguinte



clicar no botão pode ser feito do seguinte modo

  1: element = driver.find_element(:id, "mybutton")
  2: element.click

 


Muitos mais elementos existem, mas os acima apresentados são os mais comuns. Para o próximo post contínuo a iterar pelos forms existentes.


 


Bom, até à próxima.

sexta-feira, 7 de dezembro de 2012

Selenium Tutorial – Part I

Selenium é uma framework para testes de software que permite a automação de aplicações Web. Este pode ser descarregado a partir da sua página aqui. A ideia deste post é mostrar como montar um ambiente e começar a desenhar testes sobre aplicações Web. Para começar a criar testes automatizados vamos primeiro montar um ambiente de desenvolvimento destes mesmos testes. Começamos por apresentar o conjunto de utilitários usados.

Começemos pelos Browsers, vamos precisar deles para aceder às aplicações Web. Pelo menos serão necessários o Firefox e Chrome.

Para nos ajudar a encontrar as informações das páginas sobre as quais vamos automatizar testes vamos precisar do

Firebug - Firebug é uma ferramenta de desenvolvimento web que facilita o debuging, edição e monitorização de CSS de qualquer site, HTML, DOM, XHR e JavaScript;
FirePath - FirePath é uma extensão para o Firefox que adiciona uma ferramenta de desenvolvimento para inspecionar, e gerar expressões XPath, seletores CSS assim como seletores JQuery (usando o sizzle selector engine).

Além disso vamos precisar do Ruby e de um IDE minimamente decente para trabalhar. Existem várias opções como por exemplo o Eclipse, Aptana e o IntelliJ Community Edition. Qualquer um deles serve, não existindo nenhum ponto que me pareça mais benéfico num deles, em relação aos restantes para o que se pretende fazer.

Depois de termos instalado um IDE vamos precisar do Ruby que pode ser obtido a partir daqui. Depois de instalado acedam à linha de comandos e verifiquem que o mesmo está instalado usando

ruby --version

Deverão obter uma linha com um resultado deste género

ruby 1.9.2p180 (2011-02-18) [i386-mingw32]

Caso tenham problemas com a execução desta instrução experimentam verificar que a pasta dos binários foi adicionada corretamente às variáveis de ambiente. Caso contrário adicionem o caminho ás variáveis de ambiente. Para facilitar podem usar o Rapid Environment Editor que é um interface gráfico que facilita a edição das variáveis de ambientes no Windows substituindo o interface gráfico do Windows.

Após a instalação do ruby vamos instalar as gems necessários para criação de testes com o selenium-webdriver. Para tal executar as seguintes instruções

gem install selenium
gem install selenium-webdriver
gem install test-unit

no final a sua instalação pode ser verificada utilizando o comando gem list. A partir daqui, e se tudo correu bem, basta apenas abrir o IDE e começar a escrever testes. Para deixar aqui apenas um exemplo do aspecto que teria um teste em que verifiquemos por exemplo se o blog Software@test é o primeiro hint quando se pesquisa por “softwareattest” no google. Não se preocupem muito em perceber o código, o seu objectivo e explicação será alvo de posts futuros.



   1: #!/bin/env ruby
   2: # encoding: utf-8
   3:  
   4: require 'rubygems'
   5: require 'selenium-webdriver'
   6: require "test/unit"
   7:  
   8: class Hello < Test::Unit::TestCase
   9:   def blog_first_in_google
  10:     begin
  11:       #Inicialização do driver e definição do browser a usar
  12:       driver = Selenium::WebDriver.for :firefox
  13:       driver.get "http://google.com"
  14:       
  15:       #Identificação da caixa de pesquisa do google e respectiva submissão
  16:       element = driver.find_element :name => "q"
  17:       element.send_keys "softwareattest"
  18:       #Expressão Xpath para o botão "feeling luck" (popup)
  19:       submit = driver.find_element(:xpath, ".//*[@id='gsr']/table/tbody/tr/td[2]/table/tbody/tr[11]/td/div/div/span[2]/span/input")
  20:       submit.click
  21:     
  22:       #verifiação se o resultado era o que esperavamos.
  23:       assert_equal("Software@test", driver.title)
  24:     ensure
  25:       driver.close
  26:       driver.quit
  27:     end
  28:   end
  29: end



Este teste pode ser corrido a partir da linha de comandos do seguinte modo



ruby Hello.rb


Este tutorial foi feito baseando-se no ruby, mas facilmente poderia de ser adaptado a outras linguagens como Java, C#, etc. No caso de Java podem seguir um conjunto de tutoriais bastante pormenorizados para a criação de um ambiente de trabalho. Este tutorial pode ser encontrado aqui. Para melhor compreender o selenium sugiro que deem uma vista de olha na documentação oficial que pode ser encontrada aqui. Outro bom recurso é o livro Selenium Simplified do Alan Richardson.

Bom, até à próxima.

terça-feira, 4 de dezembro de 2012

Test Driven Development

Test-Driven Development (TDD), ou em português desenvolvimento orientado a testes é um processo de desenvolvimento onde os casos de teste são desenvolvidos em primeiro lugar. É um conjunto de técnicas de testes associada com Extreme Programming (XP) e pelas metodologias ágeis, sendo mesmo uma das unidades core do XP.

Existem inúmeras razões pelas quais se devem efetuar os testes primeiro lugar e só depois escrever o código do software.

Vantagens do uso de TDD

Algumas vantagens de se usar um desenvolvimento orientado a testes:

  • O Código escrito é testável
  • A equipa de desenvolvimento  passa a obter um feedback do código desenvolvido de um modo mais rápido.
  • Existe uma maior proximidade entre as especificações funcionais e o código desenvolvido. Ajuda a clarificar o comportamento. Obrigatoriedade de pensar antes de escrever o código.
  • Devemos efetuar testes sobre o interface e não sobre a implementação.
  • Os casos de testes definem exatamente o conjunto de funcionalidades necessárias tornando mais fácil identificar o código redundante, ou tarefas redundantes.
  • A existência prévia de casos de testes fornece à equipa de desenvolvimento um modo fácil de criar um primeiro esboço ou protótipo do código pretendido. Do mesmo modo que facilita os refactors futuros. O conjunto de testes existentes funcionam como uma rede de segurança e garantem que ao efetuarmos um conjunto de alterações não alteramos o comportamento da aplicação e/ou não deixamos partes da mesma sem funcionar.
  • A metodologia TDD encaixa muito bem num processo de desenvolvimento ágil. Esta metodologia acaba por levar ao desenvolvimento de código mais modularizado, flexível e mais extensível. Isto porque obriga a que se pense no código como unidades pequenas e independentes umas das outras.
  • Como se pensa primeiro nos testes antes de escrever o código que vai para produção, os testes acabam por servir de guia ao desenvolvimento das funcionalidades embora estejam longe de ser uma especificação completa. Os testes como são escritos em primeiro lugar, ainda antes de o código estar escrito, devem portanto falhar. Ao escrevermos o teste devemos garantir que este falha e que falha pelo motivo correto. A utilização de mocks ou stubs pode ajudar a simular o código a ser escrito, assim como comportamentos corretos e incorretos dos funcionalidades a escrever. Obviamente estes mocks são apenas usados para simular o comportamento do código de produção enquanto este não existe de forma a ajudar na criação dos testes.

De modo a podermos usar TDD devemos efetuar uma comparação entre o custo de desenvolvimento do software segundo esta via com o correspondente ganho na qualidade do mesmo. Existem vários artigos (mesmo livros inteiros) alertando e evidenciando para os benefícios obtidos nos projetos que geralmente usam TDD. Um desses artigos pode ser encontrado aqui. (Link artigo “About the Return on Investment of Test-Driven Development”) Um segundo artigo, também interessante,  que defende que o uso de TDD pode eliminar os custos de desenvolvimento em pelo menos 50% pode ser encontrado aqui.

Maioritariamente usam-se testes unitários como base principal do TDD dado à sua maior proximidade com o código desenvolvido. Mas não nos devemos limitar a este tipo de testes. Devemos continuar a escrever testes de verifiquem a correta integração dos mais diversos componentes. Podemos ainda ir mais longe e criar testes para os interfaces e para o modo como o utilizador interage com a aplicação. Para a criação dos testes de aceitação podemos usar frameworks como o jbehave ou o cucumber.

 

Algumas desvantagens e mitos sobre a utilização de TDD

Consegue-se criar uma bateria de testes de regressão que cubra 100% dos testes de unidade

Embora este até pareça um bom objectivo, infelizmente acaba por se tornar um pouco realística. Podem existir alguns componentes ou frameworks desenvolvidas por terceiros que não trazem uma suite de testes acoplada, muitas das vezes nem as próprias fontes do código são disponibilizadas.  Nestes casos nunca iremos conseguir dar garantias que funcionaria a 100%. Embora se possam usar técnicas de caixa preta para validar o interface usado pela nossa aplicação com a aplicação externa nunca conseguiremos validar totalmente o sistema. Por outro lado validar um interface gráfico é uma tarefa bastante difícil, geralmente a quantidade de combinações possíveis de utilização é demasiado grande, senão mesmo infinita.

Podemos ainda estar a trabalhar com sistemas, ou que partes destes,  tenham sido legados e que não existisse a prática de escrever testes nessa altura. Podemos ainda estar a lidar com equipas de desenvolvimento com pouco capacidade para criar e desenhar testes, podendo estes facilmente falhar partes importantes do sistema.

Os testes unitários formam 100% do design da especificação

Pessoas novas nesta metodologia, têm ou ficam por vezes com a percepção de que os testes unitários são suficientes para definir a especificação. È certo que os testes unitários definem uma parte da especificação de um produto. Assim como é certo que os testes de aceitação definem uma outra parte da especificação, mas estes elementos não são suficientes para formar uma especificação completa.

 

Apenas é necessário efetuar testes unitários

Em qualquer projeto de desenvolvimento, mesmos os mais simples, esta afirmação é claramente falsa. A comunidade agile é muito clara acerca da necessidade de outros tipos de testes. Os testes unitários baseiam-se em unidades simples do código não tendo em conta a sua interação com outros componentes. Pelo que muita coisa pode acontecer numa aplicação que usa vários serviços e bases de dados como suporte. Tom Fischer reflete isso muito bem no seu post sobre Unit Testing Myths And Practices.

A utilização de TDD é suficiente para os testes

TDD quando é apenas usada ao nível dos testes unitários,  é como foi referido acima insuficiente. Mesmo quando estendemos os testes realizados sobre o paradigma de TDD a testes de aceitação, sobram muitos pontos de falha possíveis numa aplicação. Situações como segurança, usabilidade, performance, escalabilidade do software entre outras estão entre alguns dos pontos que não se encaixam diretamente sobre este paradigma.

A utilização de TDD têm problemas de escalabilidade.

Embora isto seja parcialmente verdade, pode ser facilmente ultrapassado. os problemas mais comuns são:

  • Quando um projeto começa a ficar muito grande e a suite de testes começa a demorar demasiado tempo para correr, torna mais complicado correr builds frequentes se o tempo necessário para correr uma suite de testes for de uma hora, por exemplo. Por outro lado quanto mais tempo demorar o processo de execução dos testes mais tempo demoramos a receber feedback sobre o código produzido o que começa a contrariar um pouco os princípios de uma metodologia ágil que faz do rápido feedback um dos seus pontos fortes.
  • Nem todos os elementos da equipa de desenvolvimento sabem como efetuar testes ou nem todos os elementos podem estar a usar uma abordagem TDD. 

De uma forma bastante simplificada, o desenvolvimento usando TDD resume-se ao ciclo descrito no simplificado diagrama abaixo. Assim começamos sempre por “Criar um teste”. De seguida começamos por desenvolver o código necessário para passar o teste descrito. Após a conclusão do desenvolvimento deste basta correr os testes e verificar que estes passam. Se tudo correr bem podemos passar para a próximo ponto a desenvolver. Caso corra mal devemos corrigir os pontos errados e correr novamente os testes até que tudo fique OK.

tddcyclesiple

 

Existem na web alguns recursos muito bons para começar a desenvolver a partir de TDD entre os quais os seguintes parecem-me bastante interessantes e fáceis de seguir.

O Seguinte conjunto de vídeos formam um excelente tutorial sobre o desenvolvimento de software utilizando a metodologia de Test Driven Development. São mais de oito horas de vídeos mas de fácil visualização e compreensão. È usado um exemplo de desenvolvimento utilizando a plataforma Ruby on Rails e com recurso aos seguintes utilitários “Rspec”, “Cucumber,”, “Selenium” ou Watir Webdriver. Embora sejam recursos construídos sobre a plataforma Ruby on Rails são bastante simples de perceber e acredito que facilmente adaptáveis a outras linguagens.

Efficient Rails Test-Driven Development - Part I

Efficient Rails Test-Driven Development - Part II

Efficient Rails Test-Driven Development - Part III

Efficient Rails Test-Driven Development - Part IV

Efficient Rails Test-Driven Development - Part V

Efficient Rails Test-Driven Development - Part VI

 

De seguida têm outro conjunto de recurso que penso que vale a penas dar uma vista de olhos.

Bom, até à próxima.

terça-feira, 20 de novembro de 2012

Testes unitários - Praticas & Técnicas

 

Testes unitários são um método de verificação de unidades individuais de código fonte. Uma unidade pode ser considerado uma classe por completo ou apenas uma função ou procedimento.

Os testes unitários devem ser o maior grupo de testes desenvolvido para um produto. O Mike Cohen criou à alguns anos atrás uma estrutura sob a forma de uma pirâmide, do que deve ser a importância dada a cada grupo de testes. A pirâmide é semelhante à que se encontra na imagem abaixo. Podem encontrar mais informação sobre esse assunto nesse artigo.

g3028

A utilização de testes unitários é bastante benéfica para qualquer que seja o produto desenvolvido. De seguida aponto aquilo que eu considero serem os principais benefícios da sua utilização. É apenas uma opinião pessoal e não reflete necessariamente a opinião da comunidade.

Principais benefícios da utilização de testes unitários

  • A abordagem unitária (ou modular) usada por este tipo de testes elimina a dependência de outros módulos externos.
  • Conseguimos testar partes de um projeto sem existir a necessidade de todas as partes estarem concluídas ou disponíveis.
  • A equipa de desenvolvimento consegue identificar e corrigir o problema muito rapidamente, uma vez que o tempo que passou entre o momento de criação e o momento em que foi encontrado o erro é muito pouco.
  • É muito mais fácil obter uma larga cobertura do código testado permitindo assim perceber e distinguir qual o código exercitado e qual o código que nunca foi usado. O que pode acontecer porque ainda não criamos testes que façam uso dessa zona do código ou porque detectamos uma zona morta, isto é, um pedaço de código que independentemente das condições iniciais nunca será atingido.
  • Os custos associados à criação e à execução destes testes são sempre menores quando comparados com os custos de testes funcionais.
  • Permite a fácil e rápida inclusão num sistema de continuos integration.
  • A rápida eliminação dos defeitos de software leva por um lado a melhoria global dos produtos que se está a desenvolver, mas por outro lado garantir que cada módulo funciona corretamente antes de o começarmos a utilizar em conjunto com outros facilita muito o seu processo de integração num produto porque garante que os erros aqui encontrados irão corresponder a problemas de integração e não problemas na construção dos vários módulos.

Muitas das vezes as equipas de trabalho, não querem escrever os testes unitários porque não percebem o que estes são importantes ou porque têm ideias pré-concebidas sobre o assunto. Ideias como “é perda de tempo”, ou “se existirem bugs eles serão apanhados mais à frente” ou ainda “são testes a equipa de testes que os crie” entre muitas outras. De seguida apresentam-se alguns desses principais equívocos e o porque desse ponto de vista estar errado.

Alguns equívocos ou ideias tipicamente erradas acerca de testes de software :

  • Um dos principais equívocos para a não criação de testes unitários é acreditar que os bugs irão ser todos apanhados na fase de testes de integração. Mas quando os erros chegam a esta fase o tempo de resolução é sempre muito mais elevado porque já não identificamos de modo tão exato a localização do problema e temos de andar à procura do mesmo através de várias camadas ou através de vários módulos. Por outro lado é muito mais fácil atingir as diferentes possibilidades de utilização de um método através da sua utilização direta do que tentar fazer o mesmo a partir da utilização destes por outros módulos.
  • Outro grande equívoco para a não criação de testes unitários é a de que os programadores acreditam que irão perder muito tempo. Uma grande parte deles acreditam que são bons programadores e que nunca cometem erros. Mas o desenvolvimento de software é uma operação bastante complexa, e errar é uma condição da natureza humana.
  • Os erros criados são de algum modo triviais ou pouco importantes. Mas colectivamente estes podem resultar num período excessivo de integração resultando por fim numa ultrapassagem dos tempos definidos para a entrega do projeto.
    Percebendo a importância dos mesmos é necessários começar a criá-los. E existem alguns aspectos que quando tidos em conta ajudam a melhorar a qualidade dos testes desenvolvido e por consequência a sua eficácia. Dou apenas alguns exemplos daquilo que me parece importante.

Algumas boas práticas para a criação de Testes Unitários:

  • Garantir que cada teste é independente dos restantes. Um bom modo de despistar este tipo de dependências é correr sempre os testes por uma ordem aleatória. De modo a que independentemente da ordem o resultado de cada teste não pode variar.
  • Cada comportamento deve apenas ser especificado por um único teste de modo a evitar que mudanças no comportamento da aplicação obriguem à alteração de muitos testes.
  • Os vários módulos devem ser testados de forma independente de modo a evitar uma sobreposição entre os testes e o comportamento.
  • Cada teste criado deve ser de fácil leitura de modo a facilitar o trabalho a elementos que continuem o trabalho algum tempo depois. Estes devem ter um nome elucidativo da sua função, e na implementação do mesmo deve ser claro o que é que está a ser testado.
  • Antes de iniciar uma alteração importante garantir que todos os testes unitários estão a passar. De modo a termos a certeza que após as alterações ao software os possíveis problemas existentes foram por nós introduzidos.
  • Garantir que os bugs encontrados durante esta fase são corrigidos antes de iniciarmos a fase seguinte.
  • Criar um ambiente limpo para a execução de cada teste. O resultado do teste anterior não pode influenciar o resultado do teste atual apenas porque de algum modo correu mal ou deixou o ambiente em mau estado. É uma boa prática cada teste ante de executar o “core” da sua atividade colocar o ambiente numa estado conhecido e fiável. Esta situação vai garantir que os resultados obtidos são de confiança.
  • Devem ser utilizados stubs ou mocks de modo a testar de forma mais eficiente. Estes dois termos embora bastante semelhantes e muitas vezes confundidos são diferentes. Uma boa distinção entre ambos pode ser encontrado neste excelente artigo do Martin Fowler (Mocks aren’t Stubs).
  • Devem ser feitas alterações ao código usado nos testes unitários sempre que forem desenvolvidas melhorias no código alvo dos testes.

     

    Uma abordagem adequada para com os testes unitários proporciona uma rápida detecção de erros de software, numa fase em que a sua correção será bastante rápida dado que a implementação ainda está bastante fresca na mente do programador. A eficiência e a qualidade são melhor representados quanto mais cedo no ciclo de desenvolvimento estiverem os testes. Uma boa utilização de estratégias de testes, uma gestão adequada do processo de testes, e um uso adequado de ferramentas de teste ajudam a maximizar o esforço de teste. Todas estas opções ajudam a um processo de desenvolvimento de boa qualidade que beneficiam a empresa e o produto desenvolvido.

Existe um largo conjunto de ferramentas que permitem a rápida criação de testes unitários. Para algumas das linguagens de programação mais conhecidas como o Java e o C# as seguintes utilitários podem ser considerados válidas opções para a criação de testes unitários.

  • TestNG (para ser usado em Java)
  • JUnit (para ser usado em Java)
  • nUnit (para ser usado em .Net)

Por muitas das vezes que tentamos criar testes existem dependências que não estão concluídas, ou partes do software as quais não nos queremos preocupar. Podemos para esses casos criar stubs e mocks para simular esse comportamento. Alguns exemplos de aplicativos desse género são os abaixo indicados:

    Existem muitos mais recursos para além dos aqui referidos, usei apenas aqueles que já usei e que são de algum modo de utilização livre.
A utilização de testes unitários não irá apanhar todos os erros de software existentes no programa. Os testes unitários testam apenas as várias unidades de modo individual. Assim possíveis erros de integração, problemas de desempenho, problemas de usabilidades entre outros tipos de problemas não irão ser apanhados por este tipo de testes. è sempre necessário articular os testes unitários com outras atividades de teste de modo a obtermos uma cobertura adequado do produto desenvolvido.
      Existem muitos recursos na Web que ajudam a tentar compreender como é que criamos e para que servem os testes unitários, mas aqueles que mais me ajudaram a compreender o tema foram os que eu indique abaixo.

    Bibliografia

    http://www.codeproject.com/Articles/5772/Advanced-Unit-Test-Part-V-Unit-Test-Patterns

    http://xunitpatterns.com

    http://blog.stevensanderson.com/2009/08/24/writing-great-unit-tests-best-and-worst-practises/

    http://blog.goyello.com/2011/10/06/three-pillars-of-unit-tests/

    http://www.youtube.com/watch?v=szVYZOvtJks&feature=related

    http://martinfowler.com/articles/mocksArentStubs.html

    http://readwrite.com/2008/08/13/12_unit_testing_tips_for_software_engineers

    Bom, até à próxima.

    terça-feira, 28 de agosto de 2012

    Cookie Testing

     

    Cookie, ou como os brasileiros gostam de lhe chamar, testemunho de conexão, é um conjunto de dados trocados entre o browser e o servidor de páginas, colocado num arquivo (ficheiro de texto) criado no computador do utilizador. A sua função principal é a de manter a persistência de sessões HTTP. A utilização e implementação de cookies foi um adicionada ao HTTP. Este conceito foi introduzido pela Netscape, devido às consequências de guardar informações confidenciais num computador. Situação que por vezes pode não ser segura, devido ao dos utilizadores de terminais públicos, como bibliotecas, cooffe shops, aeroportos, etc.

    Um exemplo da possível utilização de um cookie, é quando um site cria esse mesmo cookie guardando informação da sua palavra chave para que você não a precise de digitar novamente quando voltar a visitar o site novamente. Também pode ser utilizado pelas páginas web para guardar as preferências do utilizador, como por exemplo quando o site lhe permite guardar os filtros aplicados por defeito a uma determinada listagem.

    Funcionamento:

    • Quando o servidor deseja ativar um cookie no cliente, envia uma linha no cabeçalho HTTP iniciada por Set-Cookie: .
    • A partir desse momento, consoante as opções especificadas pelo cookie, o cliente irá enviar no seu cabeçalho HTTP dos pedidos uma linha contendo os cookies relevantes, iniciada por Cookie: .

    Entre os parâmetros dos cookies estão: o tempo de vida (a data para o cookie "expirar a validade") e o domínio, ou grupo de páginas a que o cookie se aplica.

    Os cookies podem servir para guardar entre, outras coisas, informação do estado da sessão, ou estado da aplicação. Como filtros, home page usadas, ou mesmo o tema aplicado nas páginas.

    Uma utilização indevida dos cookies pode levar a um funcionamento incorreto das aplicações, onde o mesmo é usado. Pode mesmo proporcionar graves falhas de segurança.

    Abaixo está uma lista dos principais cenários para teste à utilização de cookies de um site. Vários casos de teste podem ser gerados a partir destes cenários através da realização de várias combinações.

    1. Verificar se a aplicação está corretamente a escrever cookies ou não.
    2. Testar para nos certificarmos que não existe informação pessoal ou sensível guardado no cookie. Caso tenha de existir este tipo de informação esta deve estar devidamente encriptada.
    3. Se a aplicação a ser alvo de testes é um website publico, deve evitar de usar cookies em demasia. Pode resultar numa perda de qualidade do tráfego caso o browser esteja constantemente a pedir pelos cookies.
    4. Fechar todos os browsers, apagar os cookies existentes e desabilitar o uso de cookies nas configurações do browser. Efetuar a navegação/utilização da parte da aplicação que faz uso de cookies, e verificar que são mostradas mensagens apropriadas. como por exemplo "Para bom funcionamento do site, por favor habilite os cookies no seu browser."
    5. Definir as opções do browser de solicitar ao utilizador permissões sempre que um cookie é  armazenado no sistema do cliente. Assim ao navegar por esta parte do site que usa cookies. O browser irá perguntar se desejamos aceitar ou rejeitar o cookie. A aplicação sobre teste deve apresentar uma mensagem apropriada se você rejeitar os cookies. Além disso deve-se verificar se as páginas que se estão possuem os dados corretos e que não existem corrupção nos dados.
    6. Feche todas as janelas do browser e apagar manualmente todos os cookies. Navegue entre as várias páginas da aplicação web e verifique que estas páginas se as mesmas mostram um comportamento inesperado.
    7. Edita alguns cookies manualmente no notepad++ ou em qualquer outro editor. Realiza um conjunto de modificações como alterar o conteúdo de cookies, nome do cookie, alterar a sua data de validade, etc. Após estas alterações volte a testar as funcionalidade do site. Efetue várias variações nos dados alterados de modo a procurar algumas falhas de segurança. Os cookies corrompidos não deve permitir a leitura dos dados dentro dele.
    8. Os cookies escritos por um website não devem de poder ser acedidos por outros.
    9. Se a aplicação que está a ser alvo de teste, de algo modo efetua transações online, deve-se verificar que no final da conclusão da operação, o cookie é devidamente apagado e que as ações futuras do utilizador não resultam numa segunda compra feita de modo inadvertido pelo mesmo utilizador.
    10. Verifique se o sistema em teste escreve os cookies de forma correta em diferentes navegadores  e o site funciona corretamente com estes cookies. Este teste pode/deve ser feito em vários navegadores, como diferentes versões do Internet Explorer, Mozilla Firefox, Netscape, Opera, Safari, etc. A W3schools possui uma lista com os browsers mais usados que é atualizada com regularidade, e que pode ser consultada aqui. Para uma aplicação generalista deve pelo menos ser verificada com as versões mais recentes dos 4-5 browsers mais usados.
    11. Se o sistema em teste usa cookies para manter o estado de sessão dos usuários. Verifique se existe algum ID que esteja a ser mostrado na barra de endereço. Agora, experimenta mudar o ID & pressione enter. Deve ser exibida uma mensagem de acesso negado e não deverá ser possível de ver a conta de outros utilizadores.

    Aplicações como Firebug permitem a edição dos cookies, pelo que basta abrir a aplicação e começar a brincar com os dados que nos são expostos e ver o que acontece.

    Para finalizar existe um muito bom artigo sobre o modo de testar o uso de cookies nas aplicações web que pode ser encontrado aqui.

    Bom, até à próxima.

    sexta-feira, 29 de junho de 2012

    Privacidade (ou a falta dela)

    Toda a gente já viu, ou pelo menos ouviu falar de um dos seguintes filmes I know what you did last summer ou a sua sequela I Still Know What You Did Last Summer. Mas o que acharias se te dissessem

    “Sei o que  fizeste à cinco minutos atrás”, ficarias a olhar para alguém com um ar preocupado, apreensivo.

    Ora experimentem ver o vídeo abaixo.

    Preocupados com o Big Brother! Tudo é possível. :)

    Vale a pena pensar nisto.

    Bom, até à próxima.

    sexta-feira, 8 de junho de 2012

    Continuous Delivery – Road to Perfection

    Segundo o Martin Fowler a Integração Contínua (CI) é uma prática de desenvolvimento de software onde os membros de uma equipa integram seu trabalho de forma  frequente, geralmente cada elemento da equipa faz essa integração pelo menos uma vez por dia. Aqui cada integração é verificada por uma compilação automatizada (incluindo um conjunto de teste) para detectar erros de integração tão rapidamente quanto possível. Esta abordagem leva a que o tempo que uma equipa demora a encontrar problemas de integração seja reduzidos significativamente levando a um desenvolvimento de software mais coeso.

    Segundo o mesmo Martin Fowler existe um conjunto de princípios básicos que se devem seguir.

    Practices of Continuous Integration

    • Maintain a Single Source Repository.
    • Automate the Build
    • Make Your Build Self-Testing
    • Everyone Commits To the Mainline Every Day
    • Every Commit Should Build the Mainline on an Integration Machine
    • Keep the Build Fast
    • Test in a Clone of the Production Environment
    • Make it Easy for Anyone to Get the Latest Executable
    • Everyone can see what's happening
    • Automate Deployment

    Assim o objectivo é criar um Sistema de CI de modo a que os erros cometidos durante o desenvolvimento sejam o mais rapidamente detectados . Assim olhando para as práticas definidas pelo Martin Fowler vamos tentar cumprir todas elas. Comecemos a analisar as mesmas e a verificar como as podemos cumprir.

    Maintain a Single Source Repository

    Deve existir um branch de desenvolvimento único por projeto em que toda a equipa deverá trabalhar.  Outros branchs apenas para correções de bugs em versões de produção antigas. Para o desenvolvimento de novas funcionalidades usar um mecanismo que permita ligar e desligar essas mesmas funcionalidades, para que estas não sejam visíveis nas versões de produção. O Martin Fowler criou um post no seu blog onde dá um conjunto de dicas bastante úteis sobre como gerir a problemáticas dos branchs. Este post pode ser consultado aqui.

    Estando a falar de repositórios deixo algumas notas sobre o que deve ser feito antes de efetuar um commit para o repositório, e que cuidados se devem ter em conta:

    • Deve atualizar a sua cópia local para obter todas as novas alterações feitas por terceiros desde a última atualização do  repositório.
    • Se existirem conflitos, estes devem ser resolvidos localmente. Não se deve simplesmente sobrepor as alterações de terceiros.
    • Deve compilar o código antes de efetuar um commit. Se não compilar, deve ser corrigido e só depois colocado no repositório.
    • Se o código compilar sem problemas, deve executar um conjunto básico de teste. Pelo menos executar os testes rápidos,  todos eles e não apenas os que você se acabou de escrever ou modificar.
    • Se um dos testes falhar, deve corrigir o código que deu origem ao erro, ou por outro lado, corrigir o teste se este já não for o mais correto. Repita o processo até que nenhum dos testes falhe.
    • Por fim deve-se efetuar o commit do código.

    Automate the Build

    Este ponto pode ser visto de dois lados, do ponto de vista do desenvolvimento e do ponto de vista do CI. Para um novo programador que se junte a uma equipa o período de tempo entre um checkout e o ter a aplicação em estado de criação de novos desenvolvimento deve ser mínimo (Entre 10-15m, a partir daí já começa a ser tempo perdido em demasia). Claro que destas estimativa se exclui-se deste tempo as instalações mais básicas como SO, IDE de desenvolvimento e ambientes Java, .Net, Ruby. Em suma, o processos de compilação deve permitir que qualquer pessoa seja capaz de trazer uma máquina virgem, retirar as fontes do repositório, correr um único comando, e ter o sistema pronto a correr na sua máquina.

    Por outro lado devemos automatizar o processo de compilação numa máquina dedicada ao CI. Para se chegar a este objectivo existem alguns utilitários como o Ant, o Maven ou mesmo o MSBuild. Todas elas têm os seus pontos fortes e fracos. O MsBuild é muito bom para automatizar a compilação do código dos projetos .Net. È só escolher a mais adequada e começar a automatizar todo o processo de build.

    Claro que nem sempre tudo corre bem e as vezes existem falhas nas builds. Então o que devemos fazer quando estas falham?

    Primeiro é necessário identificar o que falhou. Aqui existem normalmente vários candidatos possíveis desde erros no código, a testes que não passaram, erros que não passaram ou até mesmo uma simples falta de espaço na máquina de CI, assim:

    • A primeira coisa a fazer é identificar qual é o problema.
    • Se o problema for derivado a um erro de compilação, deve-se imediatamente alertar a equipe de desenvolvimento (para que estes não atualizem a sua cópia local com o código partido), e posteriormente descobrir quem é o responsável. Verifique que este erro é corrigido o mais rapidamente possível.
    • Se o problema for derivado a testes que falharam verificar se o teste ainda se encontra adequado à situação a verificar. Deve-se adequar o mesmo caso esta situação já  não se verifique. Se por outro lado o problema estiver no código que está a ser validado por um determinado teste verificar quem é o responsável por essa parte do código e com ela verificar o problema.

    Uma build partida deve ser corrigida de forma rápida, porque geralmente está a impedir o trabalho de terceiros

    Make Your Build Self-Testing

    Tradicionalmente uma build significa compilação, empacotamento,  e todo o material adicional necessário para obter um programa para executar. Um programa pode ser compilar corretamente, até arrancar, mas isso não significa que ele faz o que correto.

    Aqui podem ser colocados testes unitários, verificações de comprimento das regras de arquitetura definidas (um utilitário bom para este aspecto é por exemplo o Macker embora seja uma aplicação já descontinuada). Um outro exemplo de uma aplicação que nos permite efetuar verificações de arquitetura no código desenvolvido é o Sonar que pode facilmente ser integrado com servidores de continuous integration ou mesmo com um IDE de desenvolvimento. Podemos/devemos colocar mesmo testes funcionais e de integração.

    Mais Informação sobre como forçar regras de arquitetura no código desenvolvido pode ser encontrada aqui.

    Comecemos por uma abordagem sobre análise estática do código e por alguns utilitários conhecidos que permitem identificar os erros mais comuns na construção de software. O FindBugs e o PMD são dois bons exemplos para identificar este tipo de erros. Embora tenham uma parte bastante semelhante estas também se complementam muito bem.

    O FindBugs é um programa open Source criado por Bill Pugh e David Hovemeyer que procura bugs em código Java. Ele usa análise estática para identificar centenas de diferentes tipos de potenciais de erros que opera sobre o bytecode do Java. O software é distribuído como uma aplicação gráfica, embora também existam plug-ins disponíveis para o Eclipse, Netbeans, Hudson, Jenkins. Por outro lado por ser usado a partir do Ant.

    O PMD verifica programas feitos em código Java e procura por potenciais problemas como: Opções try / catch / finally vazias, código não utilizado, assim como variáveis ​​locais não utilizadas. O PMD também têm a capacidade de detectar código duplicado.

    As ferramentas do tipo xUnit são certamente um ponto de partida para tornar o código testável e estes testes são normalmente rápidos para se colocar numa build. São os exemplos mais conhecidos o Junit e Nunit.

    Claro que além de testes unitários podemos colocar na build testes funcionais ou de integração e existem um conjunto interessante de ferramentas que permitem cumprir este objectivo. Assim são exemplo dessas ferramentas que se focam nos testes, e que podem e que podem com mais ou menos esforço ser usadas, o FIT, Selenium, Sahi, Watir, Fitnesse  e ainda o TeStudio.

    Everyone Commits To the Mainline Every Day

    A utilização de commits frequentes obriga os programadores a dividirem o seu trabalho em pequenas porções. Esta opção ajuda a que se tenha uma melhor percepção do progresso. Geralmente ficamos com a impressão que não conseguimos fazer algo significativo no espaço de algumas horas. Mas uma boa organização, prática e metodologia ajuda-nos a conseguir cumprir estes objectivos.

    Faz bastante sentido efetuar fusões frequentes porque estas são mais fáceis de integrar do que grandes, os ocasionais. Quanto menos vezes nós efetuarmos um commit  a integração torna-se mais doloroso. Os problemas irão aparecer muito mais tarde, e irá ser gasto muito tempo na integração, porque vamos cair em “merges” complicados o que tornará sempre mais difícil descobrir o que quebrou a build.  Porque estaríamos literalmente à procura de uma agulha num palheiro.

    Every Commit Should Build the Mainline on an Integration Machine

    Quando assim acontece ficamos logo a saber que foi que provocou um erro na build. Com a associação a um sistema de notificações que partiu a build fica a saber dessa situação uns minutos depois enquanto a memória ainda se encontra fresca e o problema é mais fácil de corrigir.

    Este é provavelmente um dos item mais complicado de atingir, quando os recursos físicos do sistema são limitados. Existem opções de em vez de  o fazer a cada commit o fazer apenas a cada X horas, mas claros estes factos dependem sempre do número de elementos envolvidos, da complexidade do sistema de build montado e da capacidade da(s) máquinas associada(s) ao servidor de integração contínua.

    Keep the Build Fast

    Um dos principais pontos da Integração Contínua é fornecer um feedback rápido, e nada é mais inoportuno para uma atividade de CI do que uma build que leva muito tempo a completar. Uma build de CI deve além de compilar o código, efetuar a atualização dos ambientes de desenvolvimento e testes de modo a que se esteja a trabalhar sobre as versões mais recentes do software. Nestes casos o processo de build e mais principalmente o processo de deploy deve ser  o mais rápido possível para não ter o sistema indisponível durante um longo período de tempo.

    Uma build de CI deve conter a compilação e deploy dos dados nos ambientes de teste (potencialmente nos ambientes de desenvolvimento, se conseguirmos ter o tempo de downtime extremamente curto). Em teoria uma build de continuous integration não deve ultrapassar os dez minutos.

    Test in a Clone of the Production Environment

    Um dos objectivos dos testes é identificar e eliminar, sob condições controladas, qualquer problema de que o sistema possa vir a ter em produção. Se for usado um ambiente diferente, cada diferença resulta num risco de algo que acontece em testes não vai acontecer em versões de produção. Como resultado queremos ter uma versão tão parecida quanto possível à versão de produção pois só assim são identificados os problemas reais e que realmente importam.

    Para facilitar a criação de testes devem ser criadas user stories que simulem situações importantes para a utilização. Neste ponto será interessante experimentar as sugestões que o James Whittaker apresenta no livro Exploratory  Testing para criar guias automatizados. Estes tours como são chamados no livro ajudam a criar um bom conjunto de situações de teste que, obviamente, depois têm de ser automatizados, o que pode não ser fácil.

    Make it Easy for Anyone to Get the Latest Executable

    Uma das partes mais difíceis do desenvolvimento de software é conseguir ter certeza de que estamos a criar o software certo. Não é fácil, aliás é bastante difícil, especificar os objectivos com  antecedência e fazer a coisa correta: Normalmente as pessoas acham mais fácil de ver algo, mesmo que não está certo, e dizer como é que pode ser mudado ou melhorado. Um dos objectivos dos processos de desenvolvimento ágeis explicitamente tenta tirar proveito desta vertente do comportamento humano.

    Quero instalar a última build onde a obtenho? Acho que basta para isso ter uma qualquer pasta partilhada na rede, de modo que acedendo à mesma seja fácil de obter

    Everyone can see what's happening

    CI gira à volta de uma boa comunicação, assim é esperado que o sistema garanta que todos possam facilmente ver o estado do mesmo, e que alterações foram feitas a ele. A utilização de emails de notificação quando uma build corre bem ou mal com os resultados dos testes é sempre uma necessidade (Principalmente quando esta falha para que possa ser reposta o mais rapidamente possível).

    Automate Deployment

    Para levar as práticas de CI a bom porto são sempre necessários vários ambientes, um para executar testes de confirmação, um ou mais para executar testes secundários. Como normalmente se está a movimentar artefactos entre a máquina de CI e os ambientes de teste com uma grande frequência, este processo deve ser feito de forma automática.

    Um boa consequência de se ter o deploy automático, é que estes são depois facilmente adaptados para serem usados em ambientes de produção. Quando se automatizar os deploys em ambientes de produção será sempre bom fazer o mesmo com os processo de backup e rollback, porque infelizmente as coisas de vez em quando acabam por correr mal (O Murphy anda sempre à espreita. Sorriso).

    Outra preocupação, geralmente, é a atualização das bases de dados associados ao software desenvolvido, quando são feitas releases frequentes. A utilização de utilitários como o mybatis-migrations ajuda a melhorar em muito este aspecto. O Martin Fowler e o Pramod Sadalage escreveram também um conjunto de dicas/sugestões que podem também ser bastante úteis e que pode ser encontrado aqui.

    O que ganhamos com tudo isto?

    Em geral, o maior benefício maior e mais abrangente de Integração contínua é a redução do risco, que advém da seu rápido feedback. O CI não faz com que os bugs desaparecem de vez, mas torna muito mais fácil a sua detecção e remoção. Estes quando chegam ao produto final levam a que o utilizador fique de algum modo zangado com o fornecedor do software, o que deteriora a imagem do mesmo.

    terça-feira, 5 de junho de 2012

    Utilização do Dropbox para Sincronização dos Drafts do Windows Live Writer

    Toda a gente gosta de escrever alguns posts, uns artigos de quando em vez para colocar no(s) seu(s) blog(s). Mas por vezes esses artigos demoram imenso tempo a escrever e é útil fazê-lo em diversas localizações (Casa, Trabalho, Portátil, etc).
    No link abaixo têm um modo simples de sincronizar todos os computadores, sem ter a necessidade de andar a copiar ficheiros de um lado para o outro.
    http://www.techtipsgeek.com/dropbox-sync-windows-live-writer-local-drafts-across-computers/13116/
    Enjoy.

    PS: Podem efectuar o download da dropbox a partir daqui.

    terça-feira, 15 de novembro de 2011

    GTAC 2011: Opening Keynote Address - Test is Dead

    Deixo-vos aqui um vídeo de uma apresentação feita na abertura da GTAC deste ano sobre o qual vale a pena pensar.

    http://www.youtube.com/watch?v=X1jWe5rOu3g&list=PLBB2CAFDDBD7B7265&index=1&feature=plpp_video.

    Temos aí um conjunto de novas funcionalidades a pensar para implementar. Todas elas parecem interessantes mas será que trarão mesmo mais valias ao utilizador final.

    Deixo-vos também aqui a página pessoal do apresentador, de modo que possam procurar mais informação sobre o tema.

    A questão que aqui se põe, é a seguinte. Mais do que testar uma aplicação de ponta a ponta de forma correta. É como testar uma ideia. Como garantir que estamos a construir o sistema que os utilizadores precisam?

    Aceitam-se sugestões que ajudem a não construir aquilo que na realidade não é preciso.

    Bom, até à próxima.