sexta-feira, 14 de novembro de 2008

Novo Blog

Devido a questões de usabilidade, estou migrando meu blog e todas as postagens para o wordpress. Não deixe de visitar o novo espaço: http://germanofronza.wordpress.com/

Obrigado.

quinta-feira, 13 de novembro de 2008

Um domínio mais limpo...

A abordagem Domain-Driven-Design (ou apenas DDD), dentre várias boas características, tem contribuido para resgatar os originais valores das linguagens orientadas a objetos. Porém, isso só é possível graças aos "novos" frameworks baseados em classes POJOs, onde não somos obrigados a extender classes que não conhecemos. Agora as classes são baseadas em metadados através de anotações no próprio código Java, provendo maior flexibilidade devido ao desacoplamento com classes de frameworks.
Dentre todas as classes que compõem um domínio que faz uso de um mecanismo de persistência como a JPA, destacam-se as classes de entidades (Entity). Essas classes, que devem ser um espelho (orientado a objetos) da estrutura das entidades do banco de dados relacional, podem conter métodos de negócio que alteram ou acessam informações da base de dados. Mas para que esse acesso à base de dados dentro das classes de entidades possa ser feito, é necessário que todos os seus atributos estejam mapeados, para que quando instâncias dessas classes forem obtidas através do contexto de persistência (EntityManager ou mesmo uma Session do Hibernate Core), seus atributos também estejam no estado gerenciado (managed state). Se as classes de entidades forem implementadas dessa forma, não é necessário poluirmos elas com acesso à repositórios (implementados como DAOs, por exemplo). A Listagem 1, exemplifica a implementação de uma classe de entidade com todos os atributos mapeados.

@Entity
public class Curso {
@Id
@GeneratedValue
private Long id;

@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name="Matricula",
joinColumns=@JoinColumn(name = "idCurso"),
inverseJoinColumns=@JoinColumn(name = "idAluno"))
private List alunos;

public Long getId() {
return this.id;
}

/** retorna uma lista read-only */
public List getAlunos() {
return Collections.unmodifiableList(this.alunos);
}

public boolean existeOuJaExistouAluno(Pessoa p) {
for(Pessoa aluno : this.alunos) {
if (p.equals(p) { // por seguir o pattern Entity, compara pelo ID.
return true;
}
}

return false;
}

public void matriculaPessoa(Pessoa p) {
this.alunos.add(p);
}

public void cancelaMatriculaAluno(Pessoa p) {
this.alunos.remove(p);
}
}
Nesta classe, é possivel notar que não a poluimos com getters e setters desnecessários. Para alterar o estado desse objeto, utilizamos os métodos de negócio nela contidos. A Listagem 2 mostra a implementação de um método que possui uma lista de pessoas e precisa verificar quais estão matriculadas em determinados cursos.

Setor s = repositorioSetor.getById(2L);
List
alunosDosCursosDoSetor = new ArrayList();
for(Pessoa p : pessoas) {
for (Curso c : s.getCursos()) {
if (c.
existeOuJaExistouAluno(p) {
alunosDosCursosDoSetor.add(p);
}
}
}
Esta implementação é muito mais OO do que a implementação mostrada na Listagem 3.
Setor s = repositorioSetor.getById(2L);
List
alunosDosCursosDoSetor = new ArrayList();
for(Pessoa p : pessoas) {
for (Curso c : s.getCursos()) {
if (
existeOuJaExistouAluno(c, p) {
alunosDosCursosDoSetor.add(p);
}
}
}
O método existeOuJaExistiuAluno (que tem mais aparência de função do que de método) teria que acessar o repositório de curso ou de pessoa e fazer um select para verificar se a pessoa está ou já foi matriculada no curso em questão. Isso é procedural demais!!

A idéia de ter um modelo mais orientado a objetos é que podemos ter um código muito mais próximo do negócio a ser tratado, facilitando o entendimento do mesmo tanto pelos técnicos (desenvolvedores) quanto pelos especialistas do negócio.

quinta-feira, 31 de julho de 2008

Arquiteturas...

Ao iniciar o desenvolvimento de qualquer software, uma das primeiras atividades desempenhadas pelos desenvolvedores (especialistas em computação) é a definição da arquitetura do mesmo. Essa tarefa custuma ocorrer logo após uma definição, ou melhor, do levantamento dos requisitos do negócio a ser tratado, sendo assim, o que for definido nesta etapa deverá ser considerado durante todo o tempo de vida do projeto. Acontece que essa não é uma atividade trivial. Com tantos padrões, técnicas, modas, conceitos mal entendidos, frameworks entre outros parâmetros, muitas vezes ficamos em dúvida do que de fato utilizar. Uma coisa é clara, desejamos que o código-fonte dos nossos softwares estejam cada vez mais próximo do domínio do problema, fazendo com que a comunicação entre desenvolvedores e especialistas do negócio utilize uma linguagem comum. Como uma tentativa de atingir esse nível de qualidade do código e ter uma maior aproximação dos envolvidos no projeto, o paradigma de Domain-Driven-Design (DDD) apresenta-se como uma abordagem extremamente útil e prática a ser considerada. Através do DDD, várias diretrizes podem ser seguidas para a elaboração de uma arquitetura transparente e mais próxima do domínio como desejamos. Essas diretrizes não são relacionadas apenas com o código-fonte em si, como é o caso do uso de determinados padrões de projeto, tal como o tão falado e confundido Repository. O DDD contempla também a elaboração de uma linguagem comum (chamada de Ubiquitous Language) a ser usada entre os envolvidos no projeto. Isso significa que os desenvolvedores devem nomear seus artefatos (classes, métodos, atributos, etc) de acordo com o negócio tratado. Isso também não quer dizer que os desenvolvedores não possam usar palavras do seu próprio jargão, por exemplo, o conceito de repositórios (pattern Repository) é comumente usado nessa comunicação, mas seu conceito deve estar bem claro para ambos os lados.
Outra discussão que segue é em relação às camadas da arquitetura. A tradicional dúvida, "Onde devo colocar as regras de negócio?" A tradicional separação em classes do tipo Business Object, Value Object, Data Transfer Object, Classes de controle, entre outras, nos perguntamos onde se encaixa a orientação a objetos, essa forma tradicional é muito estruturada. Esses dias conversando com um amigo no Google Talk ele me perguntou: "me diz onde eu deveria criar meu objeto 'Notícias' que vai puxar os dados no BD, usar uma DAO? e as validações, regras de negócio?". Para responder a pergunta, abaixo consta um pequeno diagrama de classes envolvidas na solução.
A classe News é a classe de domínio. Nessa solução, o padrão Repository está sendo implementado como uma DAO, porém perceba que a classe Client (classe que exemplifica o uso dessa estrutura criada, poderia ser uma action do Struts ou lógica do VRaptor por exemplo) tem associação apenas com a interface NewsRepository, isso é importante para não termos um forte acoplamento com a implementação. A classe concreta NewsDao então implementa a interface NewsRepository para conter de fato a implementação da interação dessa entidade com o banco de dados. Perceba também que a classe News não possui nenhum método de negócio digamos, mas caso existisse algum método que alterasse o estado dos atributos este deveria ser criado nessa classe, e não em uma NewsBO. Quando temos uma classe que contém os métodos de negócio e os atributos dizemos que ela possui um alto nível de coesão, que é um ponto forte dessa implementação. Quanto a questão de validação do estado dos atributos, fazendo uso de um framework como o HibernateValidator (que inclusive serviu de inspiração para a criação da JSR-303 - Bean Validator) a tarefa torna-se simples e declarativa.

Para mais informações a respeito de DDD, peço que chequem os artigos contidos na InfoQ. Nesse espaço pode ser encontrado muito conteúdo bom sobre Java, arquitetutura, entre outros.

[]s

sábado, 12 de julho de 2008

TankCoders 2, focando em agentes...

Olá pessoas,

Como já apresentado em tópicos anteriores, o meu projeto de TCC foi desenvolver um simulador de batalhas de tanques de guerra em 3D onde pudessem ser desenvolvidos e testados agentes AgentSpeak(L) do interpretador Jason. Porém, além de prover um ambiente onde os agentes pudessem atuar, propus também que o simulador pudesse ser usado por usuários remotos. Essa feature, por mais interessante que possa ser, nada tem a contribuir para o estudo de Programação Orientada a Agentes (POA). Sendo assim, essa feature será retirada do projeto TankCoders 2.

TankCoders 2
A primeira questão a ser tratada nesse novo projeto é o paradigma de programação a ser usado. Exceço de orientação a objetos em aplicações gráficas de tempo-real já pude perceber que não é o melhor caminho. Sendo assim, de início estarei fazendo uma pesquisa de como desenvolver aplicações "orientadas a performance", focando em uma elevada taxa de FPS.

Para essa segunda versão do TankCoders, outra mudança é em relação à versão da engine JME que estarei utilizando. Será usada a versão 1.0 juntamente com a versão 2.1 da JME Physics, que segundo os respectivos release-notes, muitas melhorias foram proporcionadas.

Nesse versão também serão trocados os modelos de tanques utilizados. Pretendo tentar entrar em contato com pessoas especializadas em ferramentas de modelagens 3D, vendo a possibilidade de estar criando dois modelos com uma quantidade mínima de faces. Essa questão também irá melhorar a performance do cenário pelo menos 60% em relação a versão atual.

Para um melhor aproveitamento dos estudantes de POA, nessa nova versão estarei provendo um cenário um pouco mais controlado, ou seja, sem tantas "realidades do mundo real". Na atual versão tudo é controlado pela física e percebi que se torna extremamente complexo para uma pessoa estar desenvolvendo agentes AgentSpeak(L) realmente eficientes. O simples fato de fazer o tanque virar 90º para a direita, por exemplo, já se torna complexo, pois pode que o tanque esteja subindo uma montanha e atrapalhe a realização da ação. Tudo isso ainda precisa ser especificado de forma mais detalhada.

Conforme vou tendo mais novidades em relação ao desenvolvimento da versão 2 estarei publicando neste espaço.

Obrigado pela atenção.

quarta-feira, 9 de julho de 2008

"Fim" do trabalho, apresentação do TCC

Olá pessoal,

Hoje não estarei comentando sobre alguma tecnologia estudada ou alguma reflexão sobre o mundo de desenvolvimento JavaEE como custumo fazer aqui no blog. Estarei comentando então como foi a conclusão do meu TCC representada pela apresentação do mesmo junto a banca.

Banca foi formada pelos professores:
- Avaliador da proposta: Dr. Mauro Marcelo Mattos
- Segundo avaliador: Dr. Paulo Cesar Rodacki Gomes
- Orientador: M.Sc. Dalton Solano dos Reis

Apresentação teórica
Conforme as regras para apresentação de TCCs no Departamento de Sistemas e Computação da Furb, o tempo máximo para essa apresentação é de 30 minutos. Em muitos trabalhos, essa questão de tempo não custuma ser um problema, porém não foi o que ocorreu comigo. Antes de apresentar o trabalho para a banca (dia 08/07/2008 às 21h), realizei duas prévias, sendo que a primeira delas foi supervisionada pelo meu orientador Dalton e por amigos de turma que também apresentam seus trabalhos nesse semestre. Na primeira prévia minha apresentação teórica demorou 48 minutos, nesse momento eu começei a ficar realmente preocupado. Precisava reduzir 18 minutos, sendo que não fazia ideia de como fazer isso. Seguindo a orientação do prof. Dalton e dos amigos, em especial o Israel D. Medeiros e o Eduardo Coelho, foi possível criar uma forma de apresentar o trabalho para a banca em um tempo de 31 minutos. Nessa apresentação, além de fazer uma introdução do tema escolhido e esplanar de forma clara os objetivos do trabalho, foi apresentada de forma bastante sucinta os assuntos estudados na fundamentação teórica, que foram a àrea de Ambientes Virtuais Distribuídos, Sistemas Multiagentes e por fim à parte gráfica com a engine JMonkey Engine. O grande desafio da apresentação era de fato na parte da especificação da ferramenta. O projeto desenvolvido apresentou um número de 135 arquivos .java sendo classes, interfaces e anotações. Nem todas as classes foram incluídas nos diagramas da UML utilizados (classes e estados), mas mesmo assim a maior parte delas foi ilustrada. Resolvi então apresentá-las de acordo com a separação dos módulos que desenvolvi no software. Dessa forma consegui explicar as classes em nível de pacotes e suas respectivas funcionalidades, reduzindo o tempo gasto na apresentação de forma geral. No final foquei nos resultados obtidos, onde fiz uma discução das abordagens seguidas no trabalho. Nesse momento falei sobre o uso de Orientação a Objetos e integração de diversas tecnologias diretentes em aplicações gráficas, mostrando como isso refletiu na performance do simulador. Por último apresentei as conclusões onde enfatizei que o foco não era desenvolver um jogo (um produto) mas sim prover um ambiente mais atrativo para o estudo do paradigma de Programação Orientada a Agentes (POA).

Apresentação Prática
Começei a apresentação prática com a execução do simulador (aplicativos servidor e cliente). Utilizei dois computadores, um para cada cliente representando jogadores do tipo Avatar, que controlam os tanques de guerra atraves de teclado e mouse. Nesse momento consegui mostrar algumas caracteristicas do ambiente gráfico desenvolvido, porém obtive um crash na JVM ao carregar a batalha pela segunda vez. Esse erro ocorreu durante todo o desenvolvimento e eu já estava preparado para vê-lo também na apresentação, tanto que gravei um video para mostrar caso o erro insistisse em ocorrer. O crash ocorre devido ao uso dos modelos de tanques de guerra escolhidos. São modelos extremamente pesados, sendo que não foram criados para serem usados em ambientes gráficos de tempo-real. A solução pra esse problema é desenvolver modelos espefícos para o cenário em questão, porém isso requer muita habilidade em ferramentas de modelagem 3D, que não era o foco inicial do trabalho.
Apresentei também a execução de uma batalha onde dois tanques eram controlados por agentes da ferramenta Jason, os tanques possuiam um código simples, porém foi possível mostrar o quão abstrato um código AgentSpeak(L) pode ser em relação às abordagens tradicionais.

Considerações da Banca
Ao final da apresentação, os membros da banca inicialmente fizeram bastante elogios, tanto da parte escrita (monografia) quando no desenvolvimento e o tema escolhido. Também apresentaram várias contribuições e alguns ajustes a serem feitos, onde todos foram aceitos pelo meu orientador e por mim.

Tenho a certeza de que esse trabalho contribuiu muito para o meu desenvolvimento (tanto técnico quanto pessoal). É claro que dá um alívio concluí-lo, mas com certeza vou sentir falta dessa rotina de prazos, desenvolvimento e etc. Também sei que esse trabalho não está de fato encerrado, tenho muita vontade de continuar desenvolvendo ele, em conjunto com o professor Dalton e outros que possam vir a se interessar. Sei isso, pois essa área pesquisada despertou em mim muito interesse já que tive que me esforçar muito para compreendê-la.

Para mais informações do que foi desenvolvido, o site do projeto é: http://germanofronza.googlepages.com/tankcodersproject

Slides da apresentação: http://www.inf.furb.br/~gfronza/tankcoders/Apresentacao.pdf

Muito obrigado pela atenção.

quinta-feira, 3 de julho de 2008

Next-Generation frameworks

Hoje em dia tanto se houve falar dos "Next-Generation frameworks" (também chamados de "lightweight frameworks") e como eles se diferenciam dos "antigos" (taxados de "heavyweight"). Esses frameworks antigos, caracterizados por utilizarem diversos arquivos XML de configuração, possuem uma estrutura extremamente robusta que causa um nível de baixa produtividade às equipes de desenvolvimento. Por esse motivo, os novos frameworks surgem com o intuito de extinguir os arquivos de configuração (properties ou XMLs) e trazer essas configurações, quando realmente necessárias, para dentro do código Java. Com o surgimento das Anotações no Java 5, conceitos como Inversão de Controle, Injeção de Dependências e Convensões sobre Configurações estão sendo amplamente usados, e isso de fato diminui o grau de dependências e aumenta o nível de entendimento do código. Porém, na minha visão, é nesse momento que alguns frameworks estão cometendo um novo erro: certas configurações devem mesmo estar no código Java? Qual o real motivo disso? Modismo? Empolgação? Ódio de XML ou Properties?
Surgiu a motivação para escrever este post logo após fazer um estudo (ainda não muito aprofundado) do framework de Injeção de Dependências do Google chamado Guice (se lê: Juice). Trata-se de um framework muito interessante, simplifica diversas tarefas em relação ao seu "concorrente" Spring IoC. O problema dele é justamente na configuração que faz o bind das interfaces e implementações. O Guice tem o conceito de Modules, onde para se criar um módulo deve-se HERDAR uma classe chamada AbstractModule e sobrescrever o método configure. Esses módulos são classes Java que fazem a configuração de forma programática dos binds. Na minha visão, dois problemas surgem nesse conceito de módulo do Guice. O primeiro deles é o fato de ter que herdar de uma classe que não é sua, nem sabemos como ela é implementada. Herança é algo que deve ser evitada ao máximo! O segundo é em relação à configuração que é feita em código Java e de forma bem extensa. Se esses módulos fossem configurados em XML por exemplo, muita abstração seria proporcionada e o código Java seria limpo.

Guice video introduction: http://crazybob.org/2007/06/introduction-to-guice-video-redux.html

Só uma pequena discussão. ;D
Até.

quarta-feira, 18 de junho de 2008

Java EE de hoje...

As ferramentas tradicionais utilizadas em grande escala nas software houses são normalmente tecnologias mais antigas, pois o custo (tanto financeiro quanto recursos como tempo e equipe) para migrar sistemas legados para tecnologias atuais é muito alto. Tratando-se do mundo WEB, principalmente fazendo uso da tecnologia Java, a utilização de algum framework MVC apresenta-se como uma solução ideal para deixar as responsabilidades do software (camadas) mais isoladas, sem que uma invada o espaço da outra. Porém, como no caso de frameworks como Struts 1 e Spring, a quantidade de artefatos que precisam ser produzidos é tão alta que a produtividade começa a diminuir substancialmente. Lembrando que, no Struts 1, para cada nova action ser criada, quatro passos normalmente precisam ser executados, que são: criar a classe que representa a action, configurar o mapeamento no struts.config.xml, criar a classe de formulário e por último criar a view (jsp) para exibir o ”resultado” ao usuário. Todo esse processo é feito somente para satisfazer o framework, nada de negócio foi realmente desenvolvido até o tal momento. Facilmente percebe-se que o tempo gasto com a infraestrutura é muito alto. Outra tecnologia que atualmente é vista como proporcionadora de baixa produtividade das equipes de desenvolvimento é o EJB 2. Nele, também muitas configurações (xml) necessitam ser criadas, sendo que praticamente nenhuma convenção é utilizada.


Frameworks recentes que visam resolver os problemas


Como framework MVC, várias tecnologias recentes apresentam soluções interessantes. O próprio Struts 2 (ou Webwork), que foi lançado com características muito diferentes da sua versão anterior. Porém, pelo fato de ter seus desenvolvedores fora do Brasil, a questão do suporte e consultoria torna-se mais complicado e custoso.
Sendo assim, após um estudo dos frameworks MVC desenvolvidos por brasileiros, destaca-se o VRaptor, que tem como desenvolvedores, o pessoal de uma empresa chamada Caelum, com sede em São Paulo. Trata-se de uma empresa que além de desenvolver sistemas e frameworks, provê uma gama de treinamentos em Java. O VRaptor, que atualmente consta na versão 2.5, consiste em um framework onde primeiramente é levado em consideração “convenções” sobre “configurações”. Com ele, muitas das configurações que são feitas no Struts 1, por exemplo, são abolidas pelo uso de convensões. A criação de uma nova action com VRaptor, consiste apenas na criação de um método. Dessa forma, uma classe Java pode conter determinadas actions, sendo que as classes podem então ser nomeadas como AlunoLogic, CentroLogic, etc. As classes passam a ser vistas como um agrupamento de actions, sendo que nenhuma configuração em XML precisa ser feita, tudo funciona à base de convenções. A passagem de parâmetros da camada view para a camada de lógica (action) ocorre de forma simples, sem que nenhuma classe seja criada, como no Struts 1 que precisa ser criada uma ActionForm. A injeção dos parâmetros ocorre dentro da lista de parâmetros do próprio método.
O Quadro 1 apresenta um exemplo de código-fonte de uma classe de lógica no VRaptor.

@Component

public class AlunoLogic {


@Transaction(required=true)

public void cadastra(Aluno a) {

alunoRepository.save(a);

}


@Transaction(required=true)

public void altera(Aluno a) {

...

}

}

Quadro 1 – Exemplo de classe de lógica VRaptor.

Para que a action cadastra seja invocada através de uma view (em um jsp), a seguinte URL deve ser submetida: “aluno.cadastra.logic”. Isso já faz com que o método cadastra da classe AlunoLogic seja chamado. A passagem de parametros também é interessante, pois basta informar a url: “aluno.cadastra.logic?aluno.nome=Teste1&aluno.dataNascimento=12/05/1990”. O VRaptor já sabe que precisa criar um objeto do tipo Aluno e povoar os atributos. Com VRaptor a integração com Ajax é muito simples, sendo que basta adicionar uma anotação acima do nome do método e o mesmo já pode ser acessado por uma requisição HTTP assincrona.
A tecnologia EJB também evoluiu muito na sua versão atual que é a 3. Uma das principais vantagens sobre a versão anterior é o uso de anotações Java para realizar as configurações necessárias, que aumentam muito a produtividade. Essa versão também contempla uma maior integração com frameworks de mapeamento objeto relacional, tal como Hibernate e Prevayler. O EJB 3 traz consigo a especificação JSR-220, que inclui a Java Persistence API (JPA). Ela deixa livre a implemenação concreta do provedor de persistência de dados, onde na maioria das vezes é utilizado o Hibernate, porém a aplicação não precisa utilizar os recursos diretamente dele, tornando-a mais independente.
Como ambiente de desenvolvimento freeware e opensource, o Eclipse Web Tools Plataform (WTP) apresenta-se como uma solução bastante prática para o desenvolvimento de aplicações web fazendo uso dos frameworks previamente citados. Tanto para se trabalhar com o VRaptor, como EJB 3, existem plug-ins gratuitos que auxiliam o desenvolvedor na criação de tarefas rotineiras.
Por último, a empresa Caelum, cujo site já foi apresentado, possui diversos treinamentos. Todos os professores são certificados pela Sun e são altamente ativos na comunidade Java (nos fóruns principalmente). A maioria dos professores da Caelum sao os responsáveis pela elaboração da revista Mundo Java, que é bem conhecida no Brasil todo. Os treinamentos envolvem desde orientação a objetos e modelagem de arquiteturas, até a utilização de frameworks consagrados, incluindo o VRaptor.
Além das tecnologias, para uma equipe de desenvolvedores Java ser mais ágil, é necessário que uma metologia de desenvolvimto ágil seja considerada. Conceitos como Domain Driven Design (DDD) são muito bem vindos, fazendo com que o código-fonte da aplicação por completo, fique muito mais próximo do domínio do negócio a ser desenvolvido, favorecendo o entendimento tanto por parte da equipe técnica (TI) quanto dos especialistas do negócio.

domingo, 20 de abril de 2008

Testes de Unidade para camada de negócios usando BDUnit

Introdução

Com a aproximação das áreas de negócio e conceitos atuais como SOA e BPM, o usuário está habilitando-se a visualizar de forma mais clara o funcionamento interno do software, e com isso garantindo que o mesmo faça o que realmente se propoe a fazer. Tendo em vista que os usuários endendem o "workflow" dos seus processos e como eles estão organizados (através de orquestração) dentro do software é imprescindível que todas as unidades que compõem os processos funcionem de acordo com o esperado.
Paralelamente a isso, o crescimento e consequente popularização das metodologias de desenvolvimento ágil, tem levado os desenvolvedores a escrever Testes de Unidade em seus projetos. Tais testes, além de servirem para validar a corrente qualidade das unidades do software, servem também como uma ótima documentação (definida programaticamente) para quem vai utilizar as unidades em produção.
Quando se pensa em Testes de Unidade é necessário ter em mente qual o nível ou camada do software eles devem atingir, isso implica diretamente na escolha das tecnologias (frameworks) e técnicas a serem utilizadas. Todas as camadas de um software podem ser testadas, tais como: camada de persistência, camada de negócios, camada de aplicação, ou até mesmo a camada de apresentação (de interface com o usuário). Dentre todas as camadas a serem testadas, destaca-se a camada de negócios. Esta, como o próprio nome sugere, diz respeito diretamente ao domínio do negócio do cliente. Os testes desta camada devem testar as ditas Regras do Negócio, elas compõem o ponto crítico do sistema, pois são estas regras que determinam o funcionamento dos processos.

Porque começar a testar o negócio pela camada de negócios e não pela camada de aplicação?

Para explicar isso imagine o seguinte dialogo entre dois desenvolvedores Java:
Des1: - Meu teste deve começar da onde (qual camada)?
Des2: - Depende, qual o foco do seu teste?
Des1: - Ah, o meu foco agora é testar a segurança da aplicação, bem como outras questões relacionadas à infraestrutura.
Des2: - Okay, então o seu teste deve começar pela camada de aplicação (testar as Actions do Struts, Logicas do VRaptor, etc). Quando seu teste diz respeito à Infraestrutura da aplicação então ela deve iniciar no mínimo pela camada de aplicação.
Des1: - Entendi, mas como vou testar a minha camada de aplicação se nela eu utilizo recursos cedidos pelo servidor de aplicação/servlet container, tipo uma Session, etc.
Des2: - Simples, utilize Mock Objects, com eles pode-se criar "falsas implementações" das classes/interfaces que são utilizadas na camada de aplicação e que não são controladas por você.
Des1: Até aí tudo bem, mas agora imagine que meu foco é testar as regras de negócio do meu cliente, por que eu não deveria continuar iniciando meus testes pela camada de aplicação?
Des2: Você não quer testar as regras de negócio do seu cliente?
Des1: Sim, por que?
Des2: Então qual o motivo de você querer poluir o código de testes com questões da infraestrutura (cheio de mock objects no meio atrapalhando a visibilidade da real intenção do teste)
Des1: Faz sentido, assim quem for ler o código de testes vai entender como o negócio deve ser usado, independente de infraestrutura.
Des2: Exatamente! :D

Testando aplicações baseadas em banco de dados (normalmente sistemas)

Agora que já sabe-se a necessidade dos testes e qual camada eles devem atingir, algumas regras precisam ser seguidas, são elas:
- um método de teste não deve depender do estado da base de dados para funcionar;
- um método de teste não deve depender de outro para funcionar;
- quando um caso de teste tiver uma complexidade consideravelmente mais alta, este deve ser dividido em pequenos métodos de teste, por exemplo: testAdicionarClienteSemEndereco() ou testAdicionarClienteCpfInvalido(), etc;
- um método de teste deve ser capaz de preparar a base de dados para que ele funcione independentemente.

Cumprindo as regras anteriores usando os frameworks JUnit4 e DBUnit 2.2

JUnit é um dos mais conhecidos frameworks para execução de testes em Java, a versão 4 permite que o programador utilize anotações do Java 5 ao invés de utilizar métodos com nomes hardcode, tais como: setUp, tearDown, test. Já o framework DBUnit é uma extensão do JUnit, e como o nome sugere, serve para testes que envolvem base de dados.
Esses tipos de testes são complicados, pois, como cumprir a regra de que um método de teste não pode depender do estado da base de dados? Imagine que deve-se testar uma rotina de faturamento, como o método de teste vai fazer para cadastrar um cliente, produto, tabela de preços, condições de pagamento, etc. Esse método de testes deveria ser capaz de fazer isso em uma única linha de código, tal como executar um script SQL para deixar a base pronta para a execução do teste. Mas será que um script SQL (gravado em um txt por exemplo) é uma idéia boa? e se a aplicação for independente de SGBD, pode ser que um script SQL comprometa demais a portabilidade. Com o DBUnit, ao invés de povoar as tabelas a partir de um script SQL, utiliza-se documentos XML para definição dos dados a serem povoados. Com esta funcionalidade, é possível cumprir todas as regras citadas anteriormente.

Criando uma classe base para os testes

Para dar suporte às classes concretas de testes (Unit Test Cases), criaremos uma classe abstrata para abstrair algumas tarefas comuns a todos os métodos de teste. Segue o código desta classe:

/**
* Classe base para os testes do sistema.
* @author Germano
* @version 1.0.0.0
*/
public abstract class MyAppTestCase {

protected TransactionManager transactionManager = new TransactionManager(HibernateUtilTDD.getSession());

/**
* Conexão com o banco de dados.
*/
private static IDatabaseTester databaseTester;

/**
* Cria um novo MyAppTestCase

* Configura o acesso a base de dados e as operações de SetUp e TearDown
*/
public MyAppTestCase() {
databaseTester = new JdbcDatabaseTester("com.mysql.jdbc.Driver",
"jdbc:mysql://localhost/myApp_tdd",
"myUser",
"myPass");

databaseTester.setTearDownOperation(DatabaseOperation.NONE);
}

/**
* Método a ser executado depois que todas as unidades de testes forem executadas.

* Responsável por realizar a desconexão com o banco de dados.
* @throws Exception
*/
@AfterClass
public static void dropDBConnection() throws Exception {
databaseTester.closeConnection(databaseTester.getConnection());
}

@Before
public void setUp() {
transactionManager.beginTransaction();
}

@After
public void tearDown() {
transactionManager.commit();
}

/**
* Coloca um novo conjunto de dados na base de dados para ser utilizado na unidade de testes.

* Obs.: Não limpa a base de dados.
* @param xmlDataSet
* @throws Exception
*/
protected void appendDataSet(String xmlDataSet) throws Exception {
IDataSet dataSet = new FlatXmlDataSet(new FileInputStream(getResourcesFolderFromTestUnit(xmlDataSet)));
databaseTester.setDataSet(dataSet);

databaseTester.setSetUpOperation(DatabaseOperation.INSERT);
databaseTester.onSetup();
}

/**
* Atualiza os registros do conjunto de dados atual.

* Obs.: Não remove os registros já cadastrados no conjunto de dados atual.
* @param xmlDataSet
* @throws Exception
*/
protected void updateDataSet(String xmlDataSet) throws Exception {
IDataSet dataSet = new FlatXmlDataSet(new FileInputStream(getResourcesFolderFromTestUnit(xmlDataSet)));
databaseTester.setDataSet(dataSet);

databaseTester.setSetUpOperation(DatabaseOperation.UPDATE);
databaseTester.onSetup();
}

/**
* Remove registros do conjunto de dados atual.

* Obs.: Remove somente os registros contidos no conjunto de dados informado no xmlDataSet.
* @param xmlDataSet
* @throws Exception
*/
protected void deleteDataSet(String xmlDataSet) throws Exception {
IDataSet dataSet = new FlatXmlDataSet(new FileInputStream(getResourcesFolderFromTestUnit(xmlDataSet)));
databaseTester.setDataSet(dataSet);

databaseTester.setSetUpOperation(DatabaseOperation.UPDATE);
databaseTester.onSetup();
}

/**
* Coloca um novo conjunto de dados a ser utilizado na unidade de testes.

* Obs.: Limpa a base de dados antes de inserir o novo conjunto de dados.
* @param xmlDataSet
* @throws Exception
*/
protected void setNewDataSet(String xmlDataSet) throws Exception {
// dataset vazio para limpar a base de dados.
IDataSet dataSet = new FlatXmlDataSet(new FileInputStream(getResourcesFolderFromUtil("empty-database.xml")));
databaseTester.setDataSet(dataSet);
databaseTester.setSetUpOperation(DatabaseOperation.DELETE_ALL);
databaseTester.onSetup();

// novo dataset definido via XML.
dataSet = new FlatXmlDataSet(new FileInputStream(getResourcesFolderFromTestUnit(xmlDataSet)));
databaseTester.setDataSet(dataSet);
databaseTester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT);
databaseTester.onSetup();
}

/**
* Obtem o diretório de resources referente ao método de
* testes que invocou o método de definir DataSet.
* @return String
*/
private String getResourcesFolderFromTestUnit(String rsrc) {
StringBuffer packageName = new StringBuffer();

// obtem a pilha de chamada de métodos (um pouco de Reflection)
Throwable t = new Throwable();
String fullClassName = t.getStackTrace()[2].getClassName();

// através do nome completo da classe (pacote.class_name) extrai a string que
// representa o diretório onde se encontram os resources.
String[] folders = fullClassName.replace(".", ">").split(">");
for (int i = 0; i < folders.length-1; i++) {
packageName.append(folders[i]);
packageName.append("/");
}

URL rsrcUrl = MyAppTestCase.class.getClassLoader().getResource(packageName.toString() + rsrc);
return rsrcUrl.toString().substring(6);
}

/**
* Obtem o diretório de resources do pacote Utils dos testes.
* @return String
*/
private String getResourcesFolderFromUtil(String rsrc) {
// obtem a pilha de chamada de métodos (um pouco de Reflection++)
Throwable t = new Throwable();
String fullClassName = "";
try {
String className = t.getStackTrace()[2].getClassName();
fullClassName = Class.forName(className).getSuperclass().getCanonicalName();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

// através do nome completo da classe (pacote.class_name) extrai a string que
// representa o diretório onde se encontram os resources.
StringBuffer packageName = new StringBuffer();
String[] folders = fullClassName.replace(".", ">").split(">");
for (int i = 0; i < folders.length-1; i++) {
packageName.append(folders[i]);
packageName.append("/");
}

URL rsrcUrl = MyAppTestCase.class.getClassLoader().getResource(packageName.toString() + rsrc);
return rsrcUrl.toString().substring(6);
}
}


TransactionManager é uma simples classe que encapsula os tratamentos de transação, para isso ela deve receber no construtor uma instância da sessão do Hibernate.
IDatabaseTester é a interface do DBUnit.

Conteúdo do arquivo empty-database.xml.
Obs: Os arquivos XML usados nos testes devem ser colocados no mesmo diretório (pacote) da classe do teste em questão. Para quem utiliza o Eclipse, uma prática comum é criar dois source folders, um para os códigos Java e outro para os resources, sendo que ambos apontem para o mesmo diretório de output.

Exemplo bem simples de um Unit Test Case utilizando esta classe base:

/**
* Unidade de casos de testes de Modelo Corporativo.
* @author Germano
* @version 1.0.0.0
*/
public class ModeloCorporativoTestCase extends MyAppTestCase {

private NucleoBusinessFactory factory;

public ModeloCorporativoTestCase() {
super();
factory = new NucleoBusinessFactory(DaoFactory.getInstance(transactionManager.getSession()));
}

@Test
public void testAdiciona() throws Exception {
// adiciona um usuário à base
setNewDataSet("modeloCorporativoAdiciona.xml");

// obtem o usuário de código 1
Usuario u = factory.getUsuarioBusiness().retorna(1L);

ModeloCorporativo m = new ModeloCorporativo();
m.setCodigo("M01");
m.setDescricao("Empresas SUL");
m.setAbreviatura("M01 - EmpSul");
m.setUsuarioCadastro(u);
m.setMascara("11.22.333");

ModeloCorporativoBusiness business = factory.getModeloCorporativoBusiness();
business.adiciona(m);
assertTrue("1 - Não adicionou o modelo corporativo", (m.getId() != 0));
}
}


Conteúdo do arquivo modeloCorporativoAdiciona.xml.

Conclusão

Dentre todas as práticas sugeridas pelas metodologias de desenvolvimento ágil, com certeza o uso de testes (principalmente usando o paradigma de Test Driven Development) é uma das mais praticadas em projetos de desenvolvimento de software. Podendo contar com poderosas ferramentas de testes, como as citadas neste artigo e outras, a produtividade da equipe de testes (ou dos próprios programadores de sistema) torna-se cada vez maior.

sexta-feira, 7 de março de 2008

TCC + TCC

Olá,

Estou a um tempo sem postar aqui no blog, a falta de tempo é o principal motivo.
Desde a data do último post, além das pesquisas na área de desenvolvimento de sistemas corporativos, tenho estudado e desenvolvido um projeto na área de Jogos 3D usando Java e é justamente isso que estarei comentando hoje.
Este projeto servirá como TCC da faculdade de Ciências da Computação da Universidade Regional de Blumenau, na qual estarei me graduando em julho deste ano de 2008.
Tal projeto prevê o desenvolvimento de um Simulador de um Ambiente Virtual Distribuído (AVD) multiusuário para batalhas de tanques em 3D. Na batalha, os tanques poderão ser controlados por usuários (humanos) ou também por agentes inteligentes programados na linguagem AgentSpeak(L) do interpretador Jason.
Encontrei motivação para o desenvolvimento de tal projeto quando começei a utilizar o Robocode e programar alguns tanques diretamente na linguagem Java. Depois de várias tentativas de criar times com uma certa "inteligência", poucas tiveram sucesso. O principal motivo disso que notei foi que Java não parece ser a linguagem ideal para escrever entidades inteligentes, o código dos tanques ficava extremamento extenso e complicado quando queria implementar determinados "planos" que os tanques tinham que executar, principalmente quando precisava utilizar técnicas de cooperação e/ou coordenação. Simultâneamente ao estudo do Robocode, na faculdade eu estava estudando uma área da IA que se denomina Programação Orientada a Agentes (POA), cuja disciplina era ministrada pelo professor Jomi F. Hubner. Nessa disciplina, estudamos diversos modelos de Sistemas Multiagentes (SMA), porém o foco era contemplar a arquitetura BDI e o interpretador Jason, que é uma ferramenta para executar códigos de agentes escritos na linguagem AgentSpeak(L). Tal linguagem, por natureza, já provê diversas facilidades para a construção de entidades inteligentes. Nessa linguagem, um agente compreende: crenças, desejos e intenções, ou seja, os agentes percebem coisas que acontecem no ambiente, baseado nisso eles podem passar a acreditar em algo (passa para a base de crenças), podem também passar a desejar algo e então ter a intenção de realizar algo. A sintaxe abstrata da linguagem é baseada em linguagens de lógica, tal como o Prolog, dessa forma, se escreve pouco e se tem bastante resultado.
Baseado no Robocode e no estudo de POA, idealizei um simulador de batalhas de tanques, porém com diversas melhorias. Nesta versão, o mundo virtual será em 3D, diferente do robocode que provê uma visualização em 2D. A programação dos tanques também será mais abstrata, pois será utilizada a linguagem AgentSpeak(L), e não mais o Java como o caso do Robocode.
Espero que esse simulador se torne um ambiente mais atrativo para a programação de agentes inteligentes, e que tal paradigma de programação se torne um pouco mais popular.

Conforme vou tendo mais resultados da implementação do simulador, vou postando aí as técnicas utilizadas e eventualmente versões preliminares do "executável".

É isso! ;D