segunda-feira, 27 de julho de 2009

Da necessidade ao sistema - parte 1

Existem diversas abordagens de processos de software disponíveis no mercado: RUP (Rational Unified Process - derivado do UP - Unified Process), MSF (Microsoft Solution Framework), XP (eXtreme Programming), essUP (Essential Unified Process, de um dos criadores da UML - Unified Modeling Language - Ivar Jacobson), dentre diversas outras. Umas têm abordagens mais formais, outras menos formais.

Independente do(s) paradigma(s) utilizado(s), o nosso objetivo como profissionais de sistemas de TI é sempre o mesmo: desenvolver software com qualidade, que atenda às reais necessidades dos nossos clientes. Esse é o mantra e parece simples, mas não é. Desenvolver software é uma tarefa árdua e, no final das contas, todos os processos / métodos acabam tendo fundamentos muito parecidos para que se consiga atingir esse objetivo.

Escreverei uma série de posts, mostrando um exemplo de um ciclo de vida completo de desenvolvimento de um sistema, desde a necessidade de negócio até parte do sistema pronto. Mostrarei nos posts subsequentes atividades que cobrem todo o ciclo de vida: entendimento do processo de negócio, identificação dos requisitos, definição e refinamento da arquitetura, análise, codificação e planejamento e execução de testes.

Seguirei práticas presentes em vários dos métodos citados no início do post, mas não seguirei nenhum à risca. Na minha visão, todos esses paradigmas são frameworks que compilam um conjunto de boas práticas, e que se mostraram eficazes em projetos de software bem sucedidos. Portanto, não importa qual o processo que se siga, o que importa é que se desenvolva um conjunto de atividades que agregue valor ao projeto.

Alguns pontos chaves que utilizarei:
  • Desenvolvimento iterativo e incremental;
  • Foco na arquitetura do projeto;
  • Preocupação em acomodar mudanças; e,
  • Testes planejados desde o início do projeto.

Todos esses pontos chaves estão presentes na maioria dos processos existentes no mercado. Eles serão as premissas que utilizarei no decorrer do projeto.

No próximo post comentarei sobre a necessidade de negócio que utilizarei como base para desenvolvimento do sistema.

Até lá.

segunda-feira, 20 de julho de 2009

O que nos torna bons profissionais?

Costumo me fazer essa pergunta com freqüência, como forma de reflexão sobre minha carreira. Sempre tive uma coisa muito clara para mim: um ponto crucial para ter uma boa vida pessoal e profissional é ter EQUILÍBRIO. A natureza é assim: tende ao equilíbrio e, quando não há, ocorrem os desastres naturais como furacões, terremotos, maremotos e por aí vai. Quando não temos equilíbrio em nossas vidas, os problemas pessoais e profissionais se afloram.

Mas quero neste post focar no lado profissional. Para encarar o mercado de trabalho, que está cada vez mais competitivo e agressivo, as pessoas precisam encontrar o ponto de equilíbrio entre duas coisas: o lado “técnico”, referente ao conhecimento necessário para realizar o seu trabalho; e o lado pessoal / político.

Não adianta nada conhecermos muito dos assuntos da nossa área, se não soubermos equilibrar esse conhecimento com o lado pessoal no ambiente de trabalho.

E é justamente no ponto do lado pessoal que quero colocar uma reflexão. Imagine que você é o gerente de um departamento. Que tipo de profissional você prefere ter na sua equipe: uma pessoa tecnicamente ótima e pessoalmente intragável, ou uma pessoa tecnicamente ruim e com bom relacionamento interpessoal? O ponto chave é o EQUILÍBRIO.

Já vi os dois cenários nas empresas em que trabalhei e nenhum deles, em minha opinião, é o ideal. É preciso ser um bom técnico (ou pelo menos ter boa capacidade de aprendizado e disposição para isso), uma vez que precisamos de conhecimento para executar nosso trabalho. Por outro lado, é preciso ser uma pessoa íntegra, que tenha bom relacionamento interpessoal no ambiente de trabalho, pois, caso contrário, essa pessoa provavelmente trará algum tipo de problema para a empresa.

Tenho a tendência a preferir uma pessoa educada, que se relaciona bem com os outros, de caráter e ética, em detrimento a um bom técnico. O nível técnico a gente melhora com treinamentos, palestras, leitura, grupos de estudo etc. Já a ética, caráter e educação vêm de berço. É booleano: ou a pessoa tem, ou não tem. Estes, eu prefiro que trabalhem longe de mim.

segunda-feira, 13 de julho de 2009

Utilizando o NUnit - Parte 2

Introdução


Neste post, continuarei a demonstrar alguns recursos da ferramenta de testes NUnit, conforme prometido na Parte 1.


Vimos como instalar e configurar um projeto de testes para ser executado no NUnit, através de um exemplo simples. Agora, nosso objetivo é explorar mais dois recursos interessantes:
  • Execução de testes que devem retornar uma Exception; e,
  • Configuração do Setup de uma classe de testes.

Execução de testes que devem retornar uma Exception

Imaginem o seguinte cenário: um usuário tenta cadastrar um novo Cliente, sem informar o RG. Como regra de negócio, o RG é uma informação obrigatória, portanto, ao tentar executar essa operação, o sistema deve retornar uma Exception.

O primeiro passo para implementarmos essa regra, é alterar a operação gravar da nossa classe Cliente para ficar assim:

23 public void gravar()
24 {
25 if (this.RG.Trim().Equals(""))
26 throw new ArgumentException("RG é uma informação obrigatória.");
27
28 // código para gravação do Cliente...
29 }

Reparem que, se o atributo RG não vier preenchido, uma nova Exception do tipo System.ArgumentException é disparada. Notem também que aqui poderíamos criar uma Exception customizada, mas foge do propósito deste post.

Agora, para conseguirmos testar esse comportamento no NUnit, vamos criar mais um caso de teste na nossa classe TestesCliente. O nome deste caso de teste será: gravarClienteSemInformarRG, onde criaremos um objeto Cliente, informaremos o Nome, não informaremos o RG e esperaremos como retorno a Exception, conforme implementada na classe Cliente. Segue o código do caso de teste:

21 [Test]
22 public void gravarClienteSemInformarRG()
23 {
24 Cliente.Cliente cliente = new Cliente.Cliente("Renato", "");
25 cliente.gravar();
26 }

Lembrando que, para que o teste apareça na IDE do NUnit, é preciso que o método esteja com o atributo [Test] configurado.

Agora, executando o método no NUnit, temos o seguinte resultado:

Figura 1: Execução do teste
gravarClienteSemInformarRG

O teste falhou. E qual a causa da falha? Simples: temos que dizer no método de teste que esperamos uma Exception! Para isso, basta incluir o atributo ExpectedException passando o nome da Exception esperada como parâmetro ("System.ArgumentException") no método de testes:

19 [Test]
20 [ExpectedException("System.ArgumentException")]
21 public void gravarClienteSemInformarRG()
22 {
23 Cliente.Cliente cliente = new Cliente.Cliente("Renato", "");
24 cliente.gravar();
25 }

Execute novamente e veja que o teste passa, pois o método gravar retorna uma Exception e é exatamente isso que queremos!

Configuração do Setup de uma classe de testes

Agora, suponhamos que eu quero criar um teste para pesquisar um cliente válido pelo seu nome. Para conseguirmos executar este teste com sucesso, precisamos que o Cliente já exista. Neste exemplo, não estou utilizando nenhum banco de dados para armazenar as informações, mas, se estivesse, seria necessário ter um registro válido no banco de dados para que o teste seja executado.

Como resolvemos isso? A primeira idéia seria criar o registro no banco de dados e executar o teste da pesquisa. Isso funciona, mas tem um problema: se alguém apagar o registro do banco de dados, o teste para de funcionar.

Para contornar isso, uma boa prática nestes cenários é criar uma massa de dados no início da execução de um teste e excluí-la no final. Ou seja, os testes devem ser consistentes, independente dos registros que já existirem na base de dados.

No NUnit isso é resolvido criando-se um método com o atributo Setup (que será executado no início da chamada de cada método de teste) e outro método com o atributo TearDown (que será executado no fim de cada método de teste). A seguir, demonstrarei o uso do Setup (o TearDown segue a mesma lógica).

No meu exemplo, criarei um método com o atributo Setup que será responsável por gravar um Cliente. Primeiro, criarei um atributo privado do tipo Cliente no escopo da classe de teste:

12 private Cliente.Cliente cliente;

Em seguida, criarei o método de Setup:

37 [SetUp]
38 public void Inicializar()
39 {
40 cliente = new Cliente.Cliente("Renato", "12345");
41 cliente.gravar();
42 }

Criarei agora o método pesquisarPorNomeValido na classe de teste, onde chamarei a operação pesquisarPorNome, da classe Cliente, passando no parâmetro nome a string "Renato", que foi o objeto criado no método Inicializar.

29 [Test]
30 public void pesquisarPorNomeValido()
31 {
32 cliente = cliente.pesquisarPorNome("Renato");
33
34 Assert.AreEqual("12345", cliente.RG);
35 }

Por fim, o método pesquisarPorNome, da classe Cliente, ficará assim:

37 public Cliente pesquisarPorNome(string nome)
38 {
39 if (nome.Equals(this.Nome))
40 return this;
41 else
42 return null;
43 }

Compilem o programa, executem o teste pesquisarPorNomeValido no NUnit e vejam que tudo funciona!

Explicando o código acima: no método Inicializar, coloquei o atributo Setup, que será executado sempre no início da chamada de todos os métodos de teste. Neste método, criei um novo objeto Cliente, de nome "Renato" e RG "12345". Reparem que não alterei minha classe "Cliente", logo o método gravar continua não fazendo nada (apenas validando se o RG foi informado). Neste ponto poderíamos, por exemplo, fazer a gravação dessas informações no banco de dados.

Após executar o método Inicializar, o método pesquisarPorNomeValido utiliza o mesmo objeto cliente (que foi criado pelo método de Setup), efetua a pesquisa pelo nome "Renato" e compara se o RG do cliente é igual a "12345". Como as strings conferem, então o teste é executado com sucesso.

Se no parâmetro nome for informado qualquer coisa diferente de "Renato", o teste falhará. O mesmo é válido se a comparação for feita com qualquer outra string diferente de "12345".

Abaixo segue o código fonte completo do exemplo.

Classe Cliente:

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Cliente
7 {
8 public class Cliente
9 {
10 public Cliente() : this(null, null)
11 {
12 }
13
14 public Cliente(string nome, string rg)
15 {
16 this.Nome = nome;
17 this.RG = rg;
18 }
19
20 public string Nome { get; set; }
21 public string RG { get; set; }
22
23 public void gravar()
24 {
25 if (this.RG.Trim().Equals(""))
26 throw new ArgumentException("RG é uma informação obrigatória.");
27
28 // código para gravação do Cliente...
29 }
30
31 public IList pesquisar()
32 {
33 // código para pesquisa de Clientes...
34 return null;
35 }
36
37 public Cliente pesquisarPorNome(string nome)
38 {
39 if (nome.Equals(this.Nome))
40 return this;
41 else
42 return null;
43 }
44
45 public void excluir()
46 {
47 // código para exclusão do Cliente...
48 }
49 }
50 }

Classe TestesCliente:

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using NUnit.Framework;
6
7 namespace TestesCliente
8 {
9 [TestFixture]
10 public class TestesCliente
11 {
12 private Cliente.Cliente cliente;
13
14 [Test]
15 public void gravarCliente()
16 {
17 Cliente.Cliente cliente = new Cliente.Cliente("Renato", "12345");
18 cliente.gravar();
19 }
20
21 [Test]
22 [ExpectedException("System.ArgumentException")]
23 public void gravarClienteSemInformarRG()
24 {
25 Cliente.Cliente cliente = new Cliente.Cliente("Renato", "");
26 cliente.gravar();
27 }
28
29 [Test]
30 public void pesquisarPorNomeValido()
31 {
32 cliente.pesquisarPorNome("Renato");
33
34 Assert.AreEqual("12345", cliente.RG);
35 }
36
37 [SetUp]
38 public void Inicializar()
39 {
40 cliente = new Cliente.Cliente("Renato", "12345");
41 cliente.gravar();
42 }
43 }
44 }

Conclusão

Com esses recursos, conseguimos resolver entre 80 e 90% das situações de testes do cotidiano. Existem ainda alguns outros recursos que podem ser testados, como o próprio atributo TearDown, para executar rotinas após a execução dos métodos de teste (por exemplo, limpar os registros de teste da base de dados).

Até a próxima.

segunda-feira, 6 de julho de 2009

Utilizando o NUnit - Parte 1

Introdução
Neste post demonstrarei como utilizar o NUnit para geração de testes unitários para a plataforma .Net. O NUnit é uma ferramenta muito útil e de fácil utilização, mas antes de descrever o seu funcionamento, comentarei um pouco a respeito de Testes de Software.
Desenvolver software é uma tarefa complexa e, para desenvolver um software com qualidade, é preciso estar atento a diversos fatores como: conseguir capturar requisitos que reflitam a necessidade de negócio, elaborar uma arquitetura flexível e escalável, utilizar um processo que suporte facilmente mudanças e por último, porém não menos importante, é preciso ter um processo de Testes muito bem definido.
O objetivo deste post não é discutir se RUP (Rational Unified Process) é melhor que XP (eXtreme Programming), ou se XP é melhor que MSF (Microsoft Solution Framework), ou se o PMBoK (Project Management Body of Knowledge) é melhor ou pior que Scrum. Até mesmo porque cada método / prática têm o seu lugar em contextos específicos. O ponto que quero enfatizar é a suma importância de ter um processo de Testes bem definido, como ponto chave para o desenvolvimento de software com qualidade. Discussões mais, digamos, calorosas sobre processos ficarão para outros posts. :-)

Processo de Teste
Também não é objetivo deste post entrar em detalhes teóricos de Testes de Software. Farei apenas um apanhado geral do processo e dos tipos de teste que podem ser aplicados no desenvolvimento de um software.
Um modelo bastante utilizado para planejamento e execução de testes é o chamado “V Model” (modelo em “V”). O “V” é para ilustrar como o processo é organizado (ver figura 1). A idéia do modelo é que, à medida que se avança nas atividades do desenvolvimento de um software, planejamos um tipo de teste específico. Seguindo a figura, uma vez que tem-se os requisitos (ou parte deles) definidos, podemos planejar os testes de Aceite (realizados pelo Cliente para validação do software) e de Sistema (realizados internamente, comparando o software desenvolvido com os requisitos definidos). Ao definir os componentes, pode-se planejar os testes de Integração (que focam no funcionamento integrado dos módulos que compõe o software) e, uma vez definidas as classes, procedures e scripts de banco de dados, é possível planejar os testes Unitários (que focam nas unidades de software a serem testadas). Neste post, comentarei sobre o NUnit que, como mencionado, é uma ferramenta utilizada para execução de testes Unitários.
Existem ainda outras abordagens para o processo de software e de testes, como o TDD (Test Driven Development). Em posts futuros, comentarei também sobre isso.


Figura 1: V-Model




Instalando o NUnit
Voltando para o foco principal do post: o NUnit é uma ferramenta “open source”, escrita em C# e pode ser encontrada no link: http://www.nunit.org/. Baixem o instalador através da opção “Download” do site. A versão utilizada neste post é a 2.5.0.9122. Execute o instalador e observe que o NUnit aparecerá no diretório “C:\Arquivos de Programas\NUnit 2.5”, caso as opções padrão não tenham sido alteradas na instalação. Um link para a IDE do NUnit será adicionado no menu “Iniciar à Todos os Programas à NUnit 2.5 à NUnit”. É esta IDE que utilizaremos para execução dos nossos testes unitários (ver figura 2).


Figura 2 – IDE do NUnit




Criando um projeto de exemplo
Neste exemplo, utilizaremos o Visual Studio 2008 como IDE de desenvolvimento, mas as versões 2003 e 2005 também podem ser utilizadas. Crie uma solution em branco e adicione um novo projeto do tipo Class Library. No meu caso, utilizei a linguagem C#.
Neste projeto, modifique o nome da classe “Class1” para “Cliente”, crie dois atributos e as operações básicas de CRUD (“Create” – gravação, “Read” – leitura, “Update” – atualização e “Delete” – exclusão). Veja o código abaixo. Essa será a classe que testaremos, utilizando o NUnit. Notem que não estou preocupado com o conteúdo de cada método.

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Cliente
7 {
8 public class Cliente
9 {
10 public Cliente(string nome, string rg)
11 {
12 this.Nome = nome;
13 this.RG = rg;
14 }
15
16 public string Nome { get; set; }
17 public string RG { get; set; }
18
19 public void gravar()
20 {
21 // código para gravação do Cliente...
22 }
23
24 public IList pesquisar()
25 {
26 // código para pesquisa de Clientes...
27 return null;
28 }
29
30 public IList pesquisarPorNome(string nome)
31 {
32 // código para pesquisa de Clientes, dado o nome...
33 return null;
34 }
35
36 public void excluir()
37 {
38 // código para exclusão do Cliente...
39 }
40 }
41 }


Criando um projeto de Testes para ser executado no NUnit
Adicione a solution do Visual Studio um novo projeto do tipo “Class Library” e dê o nome TestesCliente. Adicione uma referência ao projeto “Cliente”, clicando com o botão direito no nome do projeto e escolhendo a opção “Add reference...”. Na guia “Projects”, selecione o projeto “Cliente” e clique em “Ok”.
Criaremos agora um caso de teste neste novo projeto, para testar a inclusão de um novo cliente. Para isso, crie um método que instancie um novo objeto Cliente, atribuindo valores às propriedades Nome e RG e, em seguida, chame a operação gravar, conforme código abaixo.

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace TestesCliente
7 {
8 public class TestesCliente
9 {
10 public void gravarCliente()
11 {
12 Cliente.Cliente cliente = new Cliente.Cliente("Renato", "12345");
13 cliente.gravar();
14 }
15 }
16 }


Agora, para que essa classe seja testável pelo NUnit, precisamos fazer algumas configurações:

  1. Adicione uma referência à biblioteca do NUnit: clique com o botão direito no projeto TestesCliente à “Add Reference...” à “Browse” à nunit.framework.dll (no meu caso, a DLL encontra-se em “c:\arquivos de programas\NUnit 2.5\bin\net-2.0\tests”);

  2. Adicione a referência ao NUnit na classe (“using NUnit.Framework;” no topo do código);

  3. Adicione o atributo “TestFixture” à classe “TestesCliente”;

  4. Adicione o atributo “Test” ao método “gravarCliente”; e,

  5. Compile a solution.

Pronto! A classe está pronta para ser testada no NUnit! Veja como ficou o código final, depois dessa configuração:

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using NUnit.Framework;
6
7 namespace TestesCliente
8 {
9 [TestFixture]
10 public class TestesCliente
11 {
12 [Test]
13 public void gravarCliente()
14 {
15 Cliente.Cliente cliente = new Cliente.Cliente("Renato", "12345");
16 cliente.gravar();
17 }
18 }
19 }



Executando os testes via interface gráfica do NUnit
Nesta seção, veremos como executar os testes pela interface gráfica do NUnit.

  • Abra o aplicativo NUnit (pode ser através do atalho do menu iniciar, ou através do diretório de instalação, ambos citados na seção “Instalando o NUnit”);

  • No menu “File”, selecione a opção “Open Project...”;

  • Localize o seu projeto “TestesCliente” (pode ser a DLL ou o projeto do Visual Studio);

  • Repare que o método “gravarCliente” aparece na árvore de testes. Opcionalmente, selecione a opção do menu “View” à “Tree” à “Show CheckBoxes”;

  • Selecione o checkbox do método “gravarCliente”;

  • Clique na opção “Run”;

  • Repare que seu teste foi executado com sucesso.

Veja na figura 3 a execução do teste “gravarCliente”.

Figura 3: execução do teste “gravarCliente”.

Conclusão
É muito fácil utilizar o NUnit para execução de testes unitários. É interessante observar também a facilidade da ferramenta referente aos testes de regressão: caso uma funcionalidade seja alterada, é importante que todos os métodos já existentes sejam testados novamente. Com o NUnit, basta selecionar todos os métodos e executar os testes. Muito simples e extremamente útil para produzirmos software de melhor qualidade.
Para complementar este meu exemplo, sugiro que sejam adicionados outros casos de teste, para testar os demais métodos da classe Cliente.
Outros pontos que explorarei no próximo post:

  • Execução de testes que devem retornar uma Exception;
  • Configuração do Setup de uma classe de teste.

Até lá!

segunda-feira, 29 de junho de 2009

Resumo evento DNAD 2009

Neste meu segundo post, faço um resumo do evento DNAD 2009 (.Net Architects Day 2009), que ocorreu no último sábado, 27/06/2009.

Este foi o primeiro evento do grupo de discussão .Net Architects e foi um sucesso. A organização do evento foi impecável, todos foram muito assíduos e as palestras foram excelentes. Tanto a comissão organizadora, quanto os palestrantes estão de parabéns.

Na primeira palestra, Giovanni Bassi nos apresentou os principais conceitos de DDD (Domain Driven Design). Em seguida, Leandro Daniel palestrou sobre o Unit Application Block, que é um framework para injeção de dependências, presente na Enterprise Library da Microsoft.

A primeira palestra da parte da tarde foi do Victor Cavalcante, sobre Asp .Net MVC. Nessa palestra houveram algumas discussões interessantes sobre Web Forms X Asp .Net MVC.

O Juliano Oliveira falou sobre NHibernate. Aliás, foi o tema que particularmente mais me interessou, pois estou trabalhando bastante com este ORM (Object Relational Mapping). Foi bastante enfatizada a questão da produtividade que o NHibernate (e os demais frameworks ORM) trazem no desenvolvimento de um software.

Após a palestra do Juliano, fizemos um coffee-break que, diga-se de passagem, estava muito bom! Muitos salgados, doces e sucos. Genial!

Por fim, Mauricio Aniche palestrou sobre Testes e sua importância dentro do contexto de desenvolvimento de software.

O evento foi filmado e seu conteúdo será publicado em breve. Assim que tiver disponível, colocarei o link aqui.

segunda-feira, 22 de junho de 2009

Evento DNAD 2009 (.NET Architects Day)

Este meu primeiro post no blog é para comunicar que no próximo dia 27/06/2009 (sábado), ocorrerá o primeiro DNAD (.NET Architects Day).

Este é o primeiro evento promovido pelo grupo de discussão .NET Architects, que tem por objetivo discutir questões de arquitetura e engenharia de software na plataforma .NET.

O evento conterá palestras com os seguintes temas: DDD (Domain Driven Design), Asp .Net MVC (Model View Controller), Injeção de Dependência com Unity, NHibernate e Testes. As palestras serão ministradas por membros do grupo de discussão.

Para acessar o site do grupo, se cadastrar e poder participar das discussões e eventos, clique aqui.

O site do evento DNAD 2009 pode ser conferido aqui.

Para os que forem, até lá! Para os que não forem, fiquem atentos às discussões e encontros presenciais promovidos pelo grupo.