Cenários para Testes Automatizados com db4o

by alisson.vale 17/6/2008 21:43:00

Recentemente chegou às bancas a edição de número 09 da revista MundoDotNet. Essa edição conta com um artigo onde descrevo uma técnica interessante para administração de cenários de testes implementada com sucesso pelo meu colega Paulo Cesar Fernandes aqui na empresa. 

A técnica consiste em interceptar a execução de métodos de forma a armazenar objetos construídos durante a execução da aplicação em um banco de dados orientado a objetos. Assim, esses objetos podem ser carregados na memória no momento em que são necessários para o SetUp de testes unitários.

Na verdade, o artigo foi um pouco além de descrever a técnica. Grande parte do texto é dedicada a explicar as raízes e as várias formas em que técnicas de teste podem ser utilizadas para aumentar a qualidade do software no contexto de um projeto ágil.

Alguns tópicos e trechos do artigo

Uma nova visão para as atividades de teste de software:  A relação das atividades de teste com qualidade. Algumas das idéias de Deming que podem ser utilizadas em desenvolvimento de software. A relação de Deming com o estilo de administração japonês que levou ao Movimento Ágil e as técnicas que este movimento trouxe de forma a permitir a redução de inpeções no software.

Testes como oportunidades para aumentar a qualidade: Em projetos ágeis, testar significa criar oportunidades para que o produto absorva elementos de qualidade de forma permanente. "Um teste automatizado injeta qualidade dentro do software".

Quando testes automatizados podem aumentar a qualidade do processo:  Testes de aceitação automatizados criam as condições para melhoria do processo de desenvolvimento na medida em que estabelecem um instrumento de colaboração e de comunicação entre clientes e desenvolvedores. "o propósito de um teste de aceitação é aumentar a qualidade do processo de comunicação necessário para as atividades de análise e levantamento de requisitos, por meio de especificações executáveis".

Quando testes automatizados podem aumentar a qualidade do produto: Aqui a defesa é que o uso de TDD aumenta a qualidade interna do produto na medida em que cria as condições para a evolução sustentável do software. "o propósito do teste unitário é influenciar o design da aplicação, permitindo que ele evolua sem sofrer os danos causados pelo seu processo de degradação".

O artigo também oferece um rápido exemplo de como uma ferramenta como o Fitnesse pode criar especificações executáveis fáceis de ler e de produzir. Conforme imagem a seguir:

 




A abordagem Ágil oferece uma nova perspectiva para endereçar qualidade de software.
Acho que esse artigo dará ao leitor uma boa idéia do que isso quer dizer.

Utilizando métricas de código para melhoria no design de software

by alisson.vale 12/2/2008 19:40:00

Essa semana saiu mais uma edição da Revista MundoDotNet. Desta vez, pude dar minha contribuição com um artigo em que a idéia é relacionar "métricas de código" com "design de software".  Acho que ainda exploramos muito pouco dos benefícios que as métricas de código podem oferecer para o aumento nos níveis de qualidade do design de um software. Apesar de haver muitas ferramentas, raramente se vê alguém que efetivamente obtém alguma vantagem em utilizar as informações que elas geram.

De maneira geral, eu tento dar ali um caminho para se chegar a sintomas de problemas de design por meio da análise numérica de informações extraídas do código-fonte. Assim como um médico pode usar os números extraídos de exames - como números de plaquetas, ph, taxas de glicose e colesterol e outras centenas de índices possíveis - para diagnosticar doenças e prescrever tratamentos, desenvolvedores de software também podem. Um dos caminhos é saber como algumas medições revelam problemas no design. Obviamente que números não são suficientes para avaliar o design de um software. A avaliação subjetiva e humana sempre será necessária. Mas eles podem sim ser de grande ajuda nessa questão.

Quando falamos em "problemas de design", a primeira coisa que me vem a cabeça são os mau-cheiros de código que nos levam aos padrões de refatoração. Assim, o artigo tenta mostrar como podemos encontrar pontos de refatoração à partir de métricas coletadas no código-fonte. Além disso, há a questão de utilizar tal abordagem de modo sistêmico, criando assim alguns mecanismos de proteção contínua do design e abrindo espaço para a evolução do código de forma contínua.

Sei que muitos leitores desse blog não trabalham e também não conhecem a plataforma .Net. Mas, mesmo para eles, recomendo comprar de vez em quando a MundoDotNet (eu não trabalho com Java, mas estou sempre lendo a MundoJava). A MundoDotNet é uma revista que foca na plataforma, porém com uma boa ênfase em aspectos gerais sobre desenvolvimento (como design, arquitetura e técnicas de desenvolvimento), o que a faz ser atraente para qualquer um que leve desenvolvimento a sério.

No caso do meu artigo em específico, ele poderá ser lido e aproveitado por usuários de qualquer plataforma de desenvolvimento. Se quiser discutir sobre o artigo, é só deixar um comentário aqui ou me enviar um e-mail.

Boa Leitura! 

Auto-disciplina para usar o teclado na IDE

by alisson.vale 26/6/2007 02:48:00

Às vezes algumas idéias impressionam pela criatividade e simplicidade. Quem costuma fazer pair programming e/ou gosta de manter as mãos no teclado o máximo de tempo possível enquanto desenvolve na IDE, vai gostar do AddIn que Roy Osherove construiu: o KeyJedi. Ele exibe todas as teclas de atalho que são pressionadas enquanto se desenvolve. Ele pode ser utilizado para:

  • Produção de Screencasts - quem está assistindo não se perde quando o desenvolvedor utiliza teclas especiais para efetuar operações de forma mais rápida;
  • Aprendizado em sessões de Pair Programming - o co-piloto pode aprender alguns acessos rápidos enquanto assiste o piloto operando a IDE.
  • Auto-Disciplina - O KeyJedi permite que você "prenda" o mouse em sua janela enquanto está desenvolvendo. Ou seja, ele não deixa você usar o mouse na IDE. Isso vai te ajudar a adquirir o costume de não recorrer ao mouse enquanto está desenvolvendo. O interessante é que se você alternar para outro aplicativo o mouse é automaticamente liberado. Obviamente, você pode configurar qual a tecla de atalho que prende e solta o mouse.

Muito útil para quem quer se auto-disciplinar e ganhar produtividade enquanto desenvolve.

Tags:

Coding

Mazelas do Singleton

by alisson.vale 22/11/2006 14:39:09

O Singleton é um dos design patterns mais simples de se entender e também mais fácil de ser implementado. Ele garante que apenas uma única instância vai poder ser criada para um objeto. Seu funcionamento é bastante simples:

  • A classe singleton é definida com um construtor privado (ou protegido se for o caso);
  • A classe singleton mantém uma referência privada e estática ao objeto unicamente instanciado; 
  • Um método público e estático é disponibilizado para que os objetos que acessarão o singleton possam recuperar sua instância.

Há vários problemas com esse design. Você vai encontrar várias publicações e discussões em newsgroups sobre isso. Mas vou me concentrar no problema da dificuldade de extensibilidade e de suporte a variações para a classe singleton.

Depois de implementado e largamente utilizado em toda a sua aplicação, o que acontece se o seu singleton precisar variar? Ou seja, hoje ele trabalha de uma forma, mas agora eu me deparo com um requisito de negócio que me obriga a variar a implementação do singleton em diferentes cenários de uso da minha aplicação. É nessa hora que você se arrepende com todas as forças de ter utilizado o singleton para projetar sua solução.

Isso aconteceu comigo recentemente quando precisei adequar minha aplicação para trabalhar com um cenário de negócio diferente. Eu desenvolvi aqui na empresa uma solução de instalação para o nosso sistema, o Phidelis. Depois de usar arquivos de instalação MSI durante muito tempo, percebemos que ele não tinha os facilitadores que precisávamos para fazer uma instalação (one-step) do nosso aplicativo. Partimos então para criar o nosso próprio aplicativo de instalação. Esse aplicativo possibilita a instalação do sistema simultaneamente em várias máquinas, considerando o uso de clusters e de módulos do sistema sendo executados em ambientes diferenciados. O instalador também gerencia a instalação do sistema em vários ambientes (produção, homologação, etc).

No projeto desse aplicativo eu criei uma classe Singleton chamada ListaDeModulos que me permitia ter acesso a lista de módulos do sistema que o aplicativo instalaria no ambiente de um cliente. O código é mais ou menos o seguinte:

    public class ListaDeModulos

    {

        List<Modulo> _modulos = new List<Modulo>();

        private static ListaDeModulos _instance;

        private ListaDeModulos()

        {

            _modulos.Add(new IntranetAdministrativa());

            _modulos.Add(new PortalDoAluno());

            _modulos.Add(new PortalDoProfessor());

            _modulos.Add(new CobrancaExterna());

            _modulos.Add(new Servicos());
            
_modulos.Add(new VestibularOnLine());

   }

 

        public static ListaDeModulos GetInstance()

        {

            if (_instance == null)

                _instance = new ListaDeModulos();

 

            return _instance;

        }

       

        public List<Modulo> All

        {

            get { return _modulos; }

        }

 

        public Modulo WithId(string id)

        {

            foreach (Modulo each in _modulos)

            {

                if (each.Id.ToLower() == id.ToLower())

                    return each;

            }

           

            return null;

        }

   

No início parecia um bom design. O princípio Open-closed parecia estar atendido, já que a classe estava aberta para receber novos módulos ou para a remoção de módulos que ficassem depreciados. Bastaria para isso, criar uma nova implementação para a classe abstrata Modulo e adiciona-la à lista gerenciada pelo singleton.

O problema de design surgiu quando um novo requisito de negócio foi proposto. A empresa agora está trabalhando em uma simplificação do sistema para atingir clientes pequenos a um custo mais baixo. Esta nova versão do sistema chama-se Phidelis Express e terá uma lista de módulos completamente diferente da lista de módulos oferecida para clientes corporativos.

BAD DESIGN!

Meu projeto não atende a esse cenário. Eu tenho agora dois instaladores e a lista de módulos estará variando quando eu usar o instalador para o sistema Corp e para o sistema Express. Só que minha lista de módulos é rígida, e pior, ela é pública e acessada em várias partes do código com uma única instrução que agora deveria estar variando conforme o tipo de instalação que estiver ocorrendo:

ListaDeModulos.GetInstance().All;

Estou agora sentindo as mazelas do singleton na carne. A melhor solução agora será refatorar todo o projeto removendo o singleton e tratando a ListaDeModulos como uma abstração que pode ser implementada de formas diferentes em cenários alternativos. Muito trabalho para extender uma funcionalidade que poderia ser muito mais facilmente resolvida se o singleton não fizesse parte do projeto.

         Moral da história... Pense bem da próxima vez que precisar recorrer ao Singleton para resolver um problema de design. Você pode estar retirando toda a flexibilidade do seu projeto e dificultando a evolução do seu sistema.

Tags:

Coding | Design

Test Driven Revisited

by alisson.vale 15/3/2006 02:54:54

É impressionante como o desenvolvimento orientado a testes transforma o jeito como se escreve código. Também é impressionante como o TDD muda a concepção das coisas. Invertemos nossa forma de trabalhar - agora escrevemos testes antes do código. Nossos compiladores se transformaram de meros "Type checkers" para grandes parceiros nos frequentes processos de red-green-refactor que já estamos tão acostumados a executar. "Faça isso", "faça aquilo"...  "crie esta classe", "codifique este método", "implemente esta interface"... Outro dia, ouvindo um podcast do Kent Beck, descobri que o fato do TDD promover a qualidade do código já não é tão importante. Qualidade é um atributo momentâneo. Sobe e desce a cada release liberada.  Veja que não estou dizendo que qualidade não é importante. Muito pelo contrário, qualidade é essencial e é fato fundamental para a sobrevivência de qualquer produto. Mas qualidade não deve ser tratada como uma meta a ser alcançada a qualquer custo. Ela é uma consequência natural de um outro atributo ainda mais importante: Saúde. Como um organismo vivo, um software é saudável quando ele se defende bem de agressões externas - mudanças de requisito, novas funcionalidades, desafios tecnológicos. Software parado é software morto. Software que evolui com dificuldade é sofware convalescente.  

 "My goal is not quality software, my goal is healthy software" (K.Beck).

Mais do que promover a qualidade do software, TDD promove saúde. Great! Um ponto a mais para quem o utiliza. Mas não é só isso... como nos comerciais da Polishop, levando o TDD você ainda leva totalmente grátis a necessidade e rever todo o design de sua aplicação para que você possa se beneficiar da técnica de forma plena. Em resumo, TDD promove um melhor design. Faça a seguinte experiência: Selecione aleatoriamente uma classe qualquer do seu sistema. Você consegue testá-la em alguns minutos? Não? Então sua classe pode ter problemas de acoplamento e/ou coesão (leia mais em Michael Feathers).

 Nossa conclusão é que um teste escrito dessa forma é muito mais eficiente:

        [Test]
        public void LiquidarDocumentoComCaixaAbertoTeste()
        {

            Id idCaixa = new Id(93);
            Id idBoleto = new Id(120922);
           
Id idUsuario = new Id(2);
            const double VALOR_BOLETO = 392;

            ResultList list = new MemoryResultList();

            CaixaVO caixaVO = FactoryVO.GetCaixaVO(idCaixa, idUsuario, TestEnvironment.Hoje(), TestEnvironment.DataInDefinida());

            BoletoVO boletoVO = FactoryVO.GetBoletoVO(idBoleto, TestEnvironment.Hoje(), Boleto.STATUS_ABERTO, VALOR_BOLETO);

            Tesouraria operadorTesouraria = new Tesouraria();

            operadorTesouraria.LiquidarDocumento(caixaVO, boletoVO, TestEnvironment.Hoje(), VALOR_BOLETO, false, list);

            BoletoVO voBoletoVerificacao = (BoletoVO)list.GetObjectFromListById(typeof(BoletoVO), idBoleto);

            LiquidacaoDocumentoVO liquidacaoVO = (LiquidacaoDocumentoVO)list.GetObjectFromListByIndex(1);

            Assert.AreEqual(Boleto.STATUS_PAGO, voBoletoVerificacao.Status);
            Assert.AreEqual(idBoleto, liquidacaoVO.Boleto);

        }

Este teste é simples, mas ilustra bem como o design da aplicação pode fazer a diferença. A primeira mudança de paradigma é o uso do Dependency Injection. Push, don’t Pull! O objeto que está sendo testado recebe todos os outros objetos que colaboram com ele. Além de permitir a testabilidade da classe, essa técnica ajuda a manter o nível de acoplamento sob controle. Quanto mais objetos uma classe precisar receber, maior será a desconfiança de que algo está errado e de que as responsabilidades precisam ser redistribuídas. O objeto Tesouraria representa uma metáfora de design e sua função é selecionar e orquestrar objetos para a execução de operações co-relacionadas. Como seu estereótipo é de domínio, sua execução não depende de bancos de dados, bibliotecas de terceiros, etc. Ou seja, ela, como todas as outras desse estereótipo, é auto-testável.

E quanto ao acesso ao banco? É difícil, hoje em dia,  imaginar uma aplicação que não dependa de um banco de dados ou de um meio de armazenamento qualquer. Fazer acesso a banco em objetos de domínio não é uma boa opção. Em nossa arquitetura, o acesso a banco é feito apenas para alimentar a camada de domínio, mas não faz parte dela. Uma camada de Serviços recupera informações persistidas de uma camada DAO. Para testar a camada de serviços podemos usar Mock Objects ou utilizar um framework de cenários que criamos para gerar um cenário no banco de dados compatível com o teste que se deseja realizar. Veja o código que testa uma classe da camada de serviço com o apoio de um framework de cenários:

        [TestFixtureSetUp]
        public void CriarCenario()
        {
            ScenarioManager.RunScenario("001");
        }

        [Test]
        public void ObterBoletoTeste()
        {
            string sqlPrimeiroBoletoAberto = "SELECT TOP 1 * FROM " + BoletoVO.TABLE_NAME;
            sqlPrimeiroBoletoAberto += " WHERE " + BoletoVO.COL_STATUS + " = 'A'";

            DataRow dadosBoleto = ScenarioManager.GetInfoInTheFirstRow(sqlPrimeiroBoletoAberto);

            Assert.AreEqual(false, dadosBoleto == null, "Deveria existir um boleto no Banco de Dados neste ponto.");

           int idBoleto = Convert.ToInt32(dadosBoleto[BoletoVO.PK_NAME]);

           ServicosBoleto service = new ServicosBoleto();
           BoletoVO boletoVO = service.ObterBoleto(idBoleto);

           Assert.AreEqual(boletoVO.Id, new Id(idBoleto));
          
Assert.AreEqual(boletoVO.DataVencimento, Convert.ToDateTime(dadosBoleto[BoletoVO.COL_DATA_VENCIMENTO]));
           Assert.AreEqual(boletoVO.Status, Convert.ToString(dadosBoleto[BoletoVO.COL_STATUS]));
           Assert.AreEqual(boletoVO.ValorDocumento, Convert.ToDouble(dadosBoleto[BoletoVO.COL_VALOR_DOCUMENTO]));
       
}

        [TestFixtureTearDown]
        public void TearDown()
        {
            ScenarioManager.CleanUp();
        }

O Framework de cenários é configurado em um arquivo Xml, algo como o mostrado a seguir:

<?xml version="1.0" encoding="utf-8" ?>

<Cenarios RootScriptFolder=" \Phidelis.Src\Kernel\Financeiro\ TestesFuncionais\Cenarios\Scripts">

    <Script Id="CleanUp_01" Type="CleanUp" Description="Exclusão de todos os registros" File="CleanUp.sql" />
   
<
Script Id="001" Type="Cenario" Description="Ficha Financeira com um boleto de rematrícula aberto" />

</
Cenarios>

Este tipo de teste é categorizado como um “Teste Funcional” e ainda é um teste de desenvolvedor e não um teste de Quality Assurance. No entanto, só é executado sobre a camada de serviços, que é a camada consumida pela Interface de Usuário.

Depois de 2 anos trabalhando com TDD e mais de 1000 testes sendo executados a cada build, chegamos a importantes conclusões com relação ao nosso processo de escrever testes. Apenas com uma mudança arquitetural, poderíamos ter um aumento fenomenal na quantidade e na qualidade dos testes unitários, além de podermos executá-los de forma mais rápida e com mais frequência. As técnicas de Design realmente fazem a diferença, e são os testes unitários que nos têm levado a descobri-las e nos motivado a incorporá-las em nossos sistemas.


Tags:

Coding

Em Busca da IDE Perfeita

by alisson.vale 27/2/2006 19:57:45
Desenvolvedores que seguem técnicas ágeis para desenvolvimento de software certamente têm um outro tipo de visão no que diz respeito a como uma IDE deveria se comportar em seu dia-a-dia. Pra quem, como eu, trabalha com C# ou outras linguagens .Net, a busca pela IDE perfeita têm sido mais árdua do que, por exemplo, nossos amigos que trabalham com plataformas e, principalmente, comunidades mais maduras, como é o caso do Java.  Basicamente, nós só temos o Visual Studio e suas incontáveis versões. Eu acho que já contei umas oito ou nove. Por incrível que pareça nenhuma delas me agrada plenamente.  

O Visual Studio sempre foi uma IDE focada em desenvolvimento RAD. Aquela coisa... toneladas de opções e wizards que não servem pra muita coisa. Agora na versão 2005 houve um pequeno direcionamento para features “code-centric”. A intenção foi boa. A idéia era colocar alguns recursos interessantes, como a geração automática de alguns “stubs” para quem faz “coding by intention”, um intellisense mais completo (sensível a qualquer parte do código), snippets práticos e algum refactoring. Infelizmente, na prática, eu já desisti de usar muita coisa. As features de refactoring principalmente. É lento, só tem o básico e muitas vezes gera resultados indesejáveis.

Nada está perdido, no entanto. O Visual Studio é uma IDE completamente diferente quando você instala o add-in Resharper da Jetbrains (a mesma empresa do IntelliJ). O nível de produtividade é completamente outro. Quando do lançamento do VS 2005 muita gente chegou a dizer que não migraria sua aplicação simplesmente por que o Resharper não funcionaria nessa nova versão do VS. Algum tempo atrás chegou-se a divulgar que a JetBrains estava escrevendo uma IDE completamente nova para C#, semelhante ao IntelliJ. Estamos aguardando novidades sobre isso ansiosamente, pois ainda temos que conviver com um grande inconveniente do resharper: o consumo excessivo de memória. É quase inviável para projetos grandes. O pior é que a versão 2.0 do produto, pelos testes que fiz até agora, vai continuar – podendo até agravar - o problema de performance e consumo de recursos.

Continuo, assim, em busca da IDE perfeita. Há alguns dias venho testando o Sharp Develop 2.0 Beta 2. Estou muito impressionado com a IDE. É bem completa, leve e fácil de estender. É uma IDE open source e traz consigo a sustentação de boas práticas de toda a comunidade de desenvolvimento. Assim, nativamente, você já pode contar com uma boa integração com o Nunit, Nant, Ncover, Ndoc e com a garantia da incorporação de integração com outros bons projetos open source para a plataforma .Net. Além disso, a IDE já está preparada para trabalhar com o Mono e com outras vedetes que estão se tornando realidade no mundo open source da plataforma .Net, como o GTK#, Glade#, a linguagem Boo, ou com aquelas que provém da própria Microsoft, como é o caso do WinFx. A parte negativa do SharpDevelop fica por conta da falta de suporte para projetos web (ASP.Net). E é isso que ainda não me permite adotá-la de forma oficial por enquanto.

Em resumo... estou em busca da IDE perfeita. Na minha cabeça ela tem que ter o seguinte:

  • Suporte nativo a TDD + Code Coverage (com comportamento similar ao addin TestDriven.Net )
  • Suporte nativo a análise estática de código
  • Suporte real a refatorações (semelhante ao resharper da jetbrains)
  • Criação automática de stubs de métodos e classes - sem precisar tirar a mão do teclado, semelhante ao CTRL+. do VS ou ao ALT+Ins do resharper
  • Nada de wizards, geração de código que viola técnicas ou conceitos arquiteturais. Arrastar e soltar só para controles GUI
  • Detecção automática de namespaces não utilizadas ou de objetos sem namespace referenciada
  • Navegação rápida entre classes (semelhante ao CTRL+N ou CTRL+E do resharper)
  • Janela de View Definition (ótima feature do SharpDevelop)
  • Suporte a one-step-build (como na integração do SharpDevelop com o NAnt)
  • Seleção de texto inteligente (o CTRL+W do Resharper)
  • Inserção contextual de código (ALT+Ins do Resharper)
  • Correção inteligente de código (há algumas delas no Resharper 2.0 que vale a pena dar uma olhada)
  • Tudo isso em uma IDE muito leve e rápida

Sonho?

Tags:

Coding

Sobre o Autor

Alisson Vale Alisson Vale
Líder de Projeto da Phidelis Tecnologia.


E-mail me Send mail
      Sign in

Últimos posts

Últimos comentários

Termo de Responsabilidade

Este site apresenta apenas opiniões pessoais. Não necessariamente representa as opiniões ou práticas da Phidelis Tecnologia.

© 2008