14.10.05

Web Services via JavaScript

JavaScript. Ora aí está uma tecnologia horrível, incoerente e difícil de depurar (odeio esta palavra). No entanto é o que temos para desenvolvimento 'client side', e quando não se tem acesso a uma framework decente como ASP.Net ou Struts a sua utilização pode-se tornar incontornável. Ou seja, por vezes temos mesmo que recorrer a JavaScript. Neste artigo é feita uma demonstração de como se podem consumir WebServices nesta tecnologia. Se alguma vez tiverem que utilizar este método já ficam com uma ideia, de qualquer maneira desejo-vos boa sorte. As experiências que tive anteriormente com JavaScript deram-me cabo dos nervos.

13.10.05

Operações com Threads em Java.

Quando se trabalha com Threads em Java utiliza-se geralmente a keyword synchronized. Esta keyword indica que para aceder a um determinado método, o Thread tem que adquirir um lock. O que eu não sabia é que existe outro mecanismo que também permite manter a coerência entre vários processos leves, com recurso a uma técnica diferente. Vejam este artigo para descobrir qual é e quais as suas vantagens/desvantagens.

...e a vida fica mais fácil.

Ora aqui está uma daquelas ferramentas que nos poupam muito trabalho. O Applications Manager permite monitorizar uma variedade de sistemas com recurso a apenas uma aplicação de administração. Entre estes sistemas incluem-se JBoss, Tomcat, SQL Server, IIS, IBM WebSphere, Oracle, MySql e muitos outros. Uma lista completa dos sistemas suportados está disponível nesta página.

Existem duas versões desta aplicação, uma gratuita e outra paga. A gratuita permite monitorizar até 5 sistemas durante um período ilimitado. Ou seja, o ideal para quem tem um pequeno negócio ou trabalha numa empresa de pequena dimensão. Outra divergência em relação à versão paga é a ausência de actualizações e a possibilidade de suporte apenas por E-mail.

Um abraço,
  Gama Franco

12.10.05

Uma questão simples sobre segurança.

É um dado adquirido que grande parte das aplicações multi-utilizador têm algum mecanismo de identificação. Na maior parte dos casos este mecanismo resume-se à identificação de um utilizador através de um identificador único e uma password. Geralmente o identificador único é um ‘username’, ou algo idêntico.

No entanto alguns programadores não levam em conta que a base de dados onde se armazenam as palavras-passe, podem ser consultadas com recurso a técnicas muito simples para obtenção de acesso não autorizado. Algumas dessas técnicas estão muito bem documentadas, e um programador menos experiente terá pouca dificuldade em as utilizar.

Disto isto, parece óbvio que a informação mais sensível deverá ser codificada, ou melhor ainda, ser armazenada com recurso a técnicas que permitam a identificação sem se guardar a palavra-chave original.

Mais uma vez, irei recorrer a um artigo para explicar este ponto de vista. Nele são apresentadas algumas técnicas, e é exemplificada a sua utilização com recurso a tecnologia da Oracle. No entanto as ideias apresentadas podem ser utilizadas em qualquer plataforma de desenvolvimento.

11.10.05

Um cheirinho de Robótica.

Apesar de não ser a minha área, não deixei de achar este link interessante. Tem um excelente tutorial de como se pode construir protótipos de autómatos (Robots) rapidamente.

Para os mais interessados, vejam também este link. Aqui fala de que forma se pode construir um robot que se pode controlar através de tecnologia ‘wireless’.

Um abraço,
  Gama Franco

9.10.05

Filme de Terror

Passei a tarde de domingo a ver 'The Shining', um filme de terror realizado pelo mestre Stanley Kubrick. Mas o verdadeiro terror aconteceu quando desliguei o meu velhinho VHS e vi as previsões da RTP1 para as Autárquicas. Isaltino, Valentino e Fátima fazem parte dos eleitos...

Acho que em Kuala Lumpur estão a precisar de profissionais de TI.

Um abraço,
Gama Franco

4.10.05

Pensamento do dia

"If at first you don't succeed, find out if the loser gets anything."
Bill Lyon

3.10.05

Balde de água fria pela manhã.

Aqui vai um balde de água fria para quem pensa que não existe diferença para  a palavra “protected” entre Java e .Net. É engraçado porque pensava que o efeito era igual para ambas as tecnologias. Espero que não fiquem tão surpreendidos como eu.

Um abraço,
  Gama Franco

19.9.05

Linux chega aos portáteis

A CNET tem um artigo onde informa que a Dell passou a ter disponível um portátil com a distribuição de Linux Mandriva.

É interessante, lembro-me de ler num artigo de opinião do Linux Journal que isto seria um enorme passo em frente para este sistema operativo. Se tivermos em conta que o artigo tem mais de dois anos, esta notícia peca por ser tardia. No entanto não deixa de um marco.

O curioso é que o artigo de opinião referia que isto iria acontecer por exigência de uma grande empresa (que decidia baixar o custos de aquisição de equipamento), e que os fabricantes de portáteis teriam uma grande guerra pela frente para ganhar o contracto. O fabricante vencedor ficaria de costas voltadas com a Microsoft, mas estava atravessada a barreira. A partir daí seria uma guerra aberta para fazer chegar ao consumidor final soluções baseadas neste sistema operativo, ameaçando a hegemonia do gigante de Redmond. Afinal não é isto que está a acontecer, e parece que talvez exista uma lacuna no mercado de consumo. Ou talvez não, e nesse caso não irá haver procura para este tipo de produtos, deixando esta primeira iniciativa condenada ao fracasso.

Provavelmente só o futuro irá dizer, e não está tão distante quanto isso.

Um abraço,
  Gama Franco

15.9.05

JUnit4

Elliotte Rusty Harold fez um resumo acerca da nova versão do JUnit (versão 4). Para quem não conhece, trata-se de uma framework para testes unitários em Java.

Esta nova versão tem algumas inovações em relação à versão 3.8, algumas delas fortemente inspiradas no NUnit (biblioteca para testes unitários para .Net). Das novas alterações gostaria de realçar o facto de os testes tirarem agora partido das anotações, introduzidas no Java 1.5. Além disso agora é permitido fazer classes de testes que não derivam de TestCase. A vantagem? É simples, agora podemos ter uma classe de testes que deriva da classe que pretendemos testar, o que permite testes a métodos ‘protected’.

Vale a pena dar uma olhada para ver o que se pode esperar desta nova versão. Estou certo que vai dar muito que falar (ainda está em beta).

Um abraço,
  Gama Franco

31.8.05

Como vai o open source?

Quem me conhece sabe que tenho um carinho especial por Linux e o universo open source. No entanto confesso que estou um bocado desiludido pelo facto de ter sido prometida muita coisa à volta deste sistema, e os passos têm sido dados a um ritmo demasiado lento. Mas os progressos vão sendo feitos, principalmente com o aparecimento de algumas empresas a seguirem uma filosofia POSS (Professional Open Source Software). Tomando por exemplo MySQL, JBOSS, etc.
Também já fui mais radical, mas talvez se tenha devido ao facto de desconhecer as soluções proprietárias. Há áreas onde a Microsoft está a fazer um excelente trabalho, e os progressos impulsionados por esta empresa são de louvar (i.e. Business Intelligence).

Gostaria de salientar que os países onde a tecnologia tem tido mais sucesso encontram-se no hemisfério sul, os denominados países em vias de desenvolvimento. Portugal tem ainda muito que evoluir nesta matéria, e penso que o facto de ser prática comum a aquisição de software proprietário a custo zero através de práticas ilegais (P2P) prejudica a adopção de plataformas alternativas. Quem é que está disposto a fazer a migração de uma suite de Office numa PME, se é possível obter a custo zero a solução da Microsoft com riscos praticamente nulos? E qual a probabilidade de captar recursos para desenvolver em Unix/Linux se a maior parte dos técnicos sempre teve em casa versões proprietárias pelas quais não pagou? Ou se pagou este preço já vinha incluído no seu desktop, uma vez que os grandes fabricantes não têm por política a distribuição de raiz de software gratuito que iria baixar o preço do produto final.

Depois podemos também levantar a questão do tempo que leva a desenvolver aplicações para cada uma destas plataformas. Acredito que o Time to Market de uma solução .Net seja inferior a outra baseada em Java, que pode ser desenvolvida por inteiro recorrendo a tecnologias gratuitas (e que corre em qualquer sistema). No entanto o que interessa ponderar na maioria dos projectos é o retorno do investimento (ROI). Será que pelo facto de eu introduzir no mercado algumas semanas antes um produto cujo investimento foi muito superior terá um maior retorno? Nalgumas situações sim, noutras não. Além disso posso sempre cortar os valores das licenças e meter mais um programador no projecto. Desta forma o investimento fica retido no nosso país, o que é mais saudável para a economia nacional. Mas mais uma vez, na maioria dos casos temos a PME a comprar uma licença e a disponibilizar o software para N programadores. Desta forma não há ROI que aguente!

E temos ainda o problema do suporte. Mas este começa a ser cada vez mais um mal menor. A maioria das alternativas no mercado tem geralmente esta opção (paga). Agora será que compensa? Ora vejamos, se tiver uma licença de SIEBEL para um ambiente de desenvolvimento, outra para o de testes e mais uma para produção, e ao fim disso tudo desembolsar um determinado valor para obter suporte do fornecedor chego (claramente) a um valor exorbitante. Por outro lado posso sempre fazer o download (gratuito) do SugarCRM, fazer a instalação nos três ambientes e pagar (apenas) pelo suporte. Infelizmente não é fácil arranjar técnicos no mercado para SugerCRM, mas para SIEBEL também não! Além disso se subirmos a fasquia para bom/muito bom ficamos reduzidos a meia dúzia.


Para finalizar, e foi isto que me despertou o interesse por colocar aqui este pensamento, aconselho a leitura deste artigo de opinião. Aqui são apresentadas 5 razões para não se utilizar Linux. Não deixa de ter alguma piada.



Um abraço,
  Gama Franco

25.8.05

Comparação entre Web Services e .Net Remoting (ou Java RMI)

Neste artigo é apresentada uma breve comparação entre .Net Remoting e Web Services. O objectivo é simples, ajudar a determinar quando é que se deve optar por uma solução em detrimento da outra. O artigo é pequeno, pouco detalhado mas sempre dá algumas luzes aos menos esclarecidos nesta matéria.
Pela minha experiência sei que esta comparação também é valida quando feita entre Java RMI e Web Services.

23.8.05

Agile vs resto do mundo (Round I)

Felizmente, durante estes dois últimos anos, consegui absorver algum conhecimento em dois projectos que seguiram metodologias opostas. Para o primeiro (projecto A) foi utilizada uma metodologia baseada em Análise Orientada aos Objectos (OOA, recurso a UML). No segundo (projecto B) foi adoptada uma metodologia ágil (eXtreme Programming).
Na primeira parte deste artigo vou apresentar uma breve descrição das metodologias adoptadas em cada projecto. Na segunda irei fazer uma pequena comparação entre estas duas metodologias, enumerando as vantagens e desvantagens de cada uma delas. É claro que será apenas uma comparação baseada na minha experiência pessoal, ausente de qualquer justificação científica.

Na primeira fase do projecto A houve um levantamento de requisitos, seguido das fases de análise, desenho da arquitectura, implementação, testes e finalmente a entrada em produção. Este processo foi efectuado em espiral, ou seja, existiram sucessivas iterações entre a fase de análise e as que a sucedem.

O projecto B teve um processo de desenvolvimento bastante diferente. Existiu uma pequena fase de análise com os principais objectivos de se definir qual a tecnologia a utilizar, elaborar um calendário com diversas ‘milestones’ e acordar as linhas orientadoras do processo. Após esta breve fase começou-se o desenvolvimento, puro e duro.
Antes de se fazer uma linha de código criava-se um teste unitário (Unit Test). Se o teste falhasse (o mais comum) fazia-se o desenvolvimento da funcionalidade, e em seguida fazia-se o ‘refactoring’ do código. Depois corriam-se os testes unitários na totalidade para verificar se não se estragou nada do que estava feito até à data.
Os ‘milestones’ acordados na primeira fase foram Feature Complete (FC), Zero Bugs Bounce (ZBB), Beta 1, Beta 2 e finalmente Final Release. Em baixo faço a descrição de cada ‘milestone’.

FC: todas as ‘features’ foram terminadas até esta data, ou seja, a partir deste ‘milestone’ apenas se catalogaram e corrigiram ‘bugs’. Gostaria de realçar que neste projecto nem todos os ‘bugs’ foram resolvidos. Foi feita uma selecção de quais valeria a pena resolver ponderando a sua criticidade e os efeitos secundários da sua resolução. Além disso tentou-se sempre atrasar a resolução de um bug o mais possível, assim garantíu-se que as tarefas mais importantes eram sempre realizadas primeiro. Alias, esta filosofia aplicava-se também às ‘features’, mas este comportamento é comum a quase todas as metodologias que eu conheço.

ZBB: Quando este ‘milestone’ foi atingido os bugs a resolver tinham no máximo 24 horas. Ou seja, era obrigatório que todos os ‘bugs’ reportados num dia fossem resolvidos no dia seguinte, caso se optasse pela sua resolução.

Beta 1: Primeira versão a enviar para produção num grupo restrito de clientes. Aqui foram feitos testes de integração e de aceitação.

Beta 2: Versão para substituir a anterior no mesmo universo de clientes.

FR: “Abram uma garrafa de ‘Don Perignon’ e metam na conta do chefe”.

Para finalizar, gostaria de notar duas particularidades acerca desta metodologia.

  • O cliente faz parte da equipa de desenvolvimento.

  • As ‘features’ são acordadas através de ‘User Stories’

Isto quer dizer que o próprio processo de desenvolvimento é feito em ciclos muito curtos, e o projecto tem o comportamento de um protótipo evolutivo. Pode-se observar as fases de cada ciclo neste diagrama (autoria de J. Donovan Wells).

No próximo artigo irei fazer a prometida comparação entre estas duas metodologias, focando alguns pontos comuns em qualquer projecto. Será uma experiência realmente interessante, porque apesar do objectivo passar sempre por entregar um produto final no prazo acordado e deixar o cliente satisfeito, estas duas metodologias seguem filosofias opostas.

Gostaria apenas de salientar que irei fazer uma comparação tendo em conta o seguimento destas metodologias tal como foram descritas. Projectos com duração considerável sofrem sempre de alguns imprevistos, e quer num caso quer no outro não foram feitas análises de risco. Quando os imprevistos aconteceram... improvisou-se.

Também vou salientar que o um dos projectos ainda está a decorrer, e eu já não sou membro da equipa. O que quer dizer que na segunda parte do artigo irei especular acerca da sua conclusão, supondo sempre que esta será um sucesso (acredito que sim pois a equipa é liderada por pessoas com grandes capacidades nesta área).

22.8.05

Mais uma da autoria de Joel Spolsky

Joel Spolsky, o líder da empresa “Frog Creek Software” que utiliza uma estratégia revolucionária de recursos humanos, escreve neste artigo uma pequena comparação entre os métodos ágeis e os processos de desenvolvimento centrados na documentação. Este artigo é baseado na sua opinião pessoal, e não é alvo de uma comparação com resultados objectivos. No entanto considero uma leitura interessante para quem, tal como eu, tem dúvidas acerca de qual será a melhor abordagem para um determinado projecto. Infelizmente o artigo serve apenas para aumentar as dúvidas, mas de qualquer forma não deixa de ser uma leitura interessante.

No artigo está também uma ligação para a especificação de uma ferramenta de software desenvolvida por esta empresa. Trata-se de um documento com alguns detalhes técnicos interessantes.

PS: Aconselho vivamente a leitura regular deste blogue.

Um abraço,
Gama Franco

Maldita preguiça!

Já à algum tempo que me dá a preguiça para deixar aqui algumas ideias. Acho que posso justificar a minha falta de pró-actividade (palavra da moda nos dias que correm) com a época que estamos a passar. Este mês de Agosto não dá vontade fazer o que quer que seja. Parece que sou contagiado pelas férias dos outros, já que as minhas foram tão curtas que quase nem me lembro delas.

Para quebrar esta monotonia vou deixar aqui um breve ‘post’, de leitura fácil para recomeçar aos poucos a publicar alguma coisa. A leitura que vos proponho é uma entrevista ao autor de mais um livro de ‘Agile Project Management’, vulgo APM.

Aconselho a sua leitura numa pausa de cinco minutos antes do almoço, para abrir o apetite ;)

Um abraço,
   Gama Franco

28.6.05

Uma questão de Inteligência.

É comum revoltarmo-nos com um determinado jogo simplesmente porque a Inteligência Artificial não é lá grande coisa. A maior parte das vezes apenas somos batidos pela máquina porque esta conta com algumas facilidades, como por exemplo a produção de recursos a uma velocidade superior. Outras vezes levamos autênticas tareias porque os autores decidiram colocar o oponente com o dobro de recursos em relação ao jogador.

Isto acontece porque os algoritmos de inteligência artificial são programados a priori, e geralmente o oponente não consegue adaptar-se aos diversos comportamentos imprevisíveis do jogador. Quantas vezes não chegamos a vencer o computador utilizando sempre a mesma táctica do princípio ao fim do jogo?

Mas existe esperança para um futuro melhor. Um grupo de investigadores da Universidade do Texas encontra-se a produzir um jogo com um conceito novo. Nele o jogador é convidado a treinar um conjunto de agentes a reagir a determinadas situações, e depois poderá observar o seu comportamento em batalhas contra agentes treinados por outros jogadores.

Os agentes são treinados com recurso a Redes Neuronais, e o aperfeiçoamento da população é feita através de Algoritmos Genéticos. A ideia não é nova, mas nos produtos existentes no mercado o jogador tinha sempre que desenvolver a inteligência artificial dos agentes. Neste caso são os agentes que aprendem as tácticas indicadas pelo jogador. Talvez num futuro próximo tenhamos um PC à nossa altura.

2.6.05

Tenho esta mania...

Geralmente quando descubro uma ferramenta nova e esta é proprietária, tenho por hábito procurar uma com as mesmas funcionalidades e licença open source. É uma daquelas manias, e às vezes até descubro umas coisas engraçadas.

Para quem sofre da mesma doença, aqui vai uma lista bastante completa.

Um abraço,
Gama Franco

1.6.05

Oracle e os Database Links para... a concorrência.

Quem já trabalhou com bases de dados distribuídas, certamente já ouviu falar em 'Database Links' (DBLinks). Em Oracle um DBLink permite que o utilizador venha a aceder a uma tabela numa base de dados remota da mesma forma que acede a uma tabela local.

O que eu não sabia (chamem-me ignorante) é que o Oracle permite que se crie um DBLink para uma tabela numa base de dados de um DBMS da concorrência (i.e. MySQL, SQL Server, etc...).

Está tudo aqui.

Um abraço,
Gama Franco

30.5.05

EXISTS, IN e NULL

Ora aqui está um daqueles artigos que servem para tirar algumas dúvidas base.

Quando se aprende uma tecnologia nova, há alguns pontos que passam despercebidos, e levam-nos mais tarde a perder algum tempo de volta do google para clarificar algumas dúvidas subtis. O artigo em causa fala do comportamento dos operadores EXISTS e IN acerca de valores NULL.

Espero que vos seja esclarecedor tanto quanto foi para mim.

Um abraço,
Gama Franco

27.5.05

Os testes e o modelo de dados.

O modelo de dados é certamente uma das partes mais importantes de qualquer projecto, e é geralmente o ponto de partida da fase de desenvolvimento. Também é comum destacar-se uma camada para aceder a esses mesmos dados. No caso do Java, a maioria das vezes, utiliza-se o modelo ‘MVC’, cabendo à camada ‘Model’ esta tarefa. Nas arquitecturas de .Net a camada equivalente é denominada de ‘DataAccess’.

Quem utiliza métodos ágeis depara sempre com o mesmo problema no início de qualquer projecto: “Como é que vou testar a camada de acesso aos dados sem introduzir alterações na base de dados de testes?”
Esta dúvida surge porque, se estamos a testar a inserção dos dados de clientes num teste unitário, e noutro a verificar o número de clientes que existem no sistema, é certo que o segundo vai falhar se correr depois do primeiro. Desta forma está-se a violar o princípio do ‘Isolamento’.
Neste artigo irei sugerir uma técnica que permite testar a interacção entre o nosso programa e o modelo de dados, sem que se viole o isolamento dos testes de regressão.
A filosofia desta técnica consiste em registar todos os acessos numa única transacção, e depois de verificadas todas as condições, forçar o ‘rollback’ da mesma.

Por questões de simplicidade o exemplo será apresentado em Java recorrendo a uma ligação JDBC para MySql, mas a sua utilização em C# ou noutra linguagem orientada aos objectos é análoga. Gostaria apenas de realçar que para se utilizar esta técnica é necessário que o ‘DBMS’ ou a ‘Framework’ suporte transacções. Para uma arquitectura em Java com MySql é obrigatória a utilização de tabelas do tipo ‘INNODB’ por ser o único que suporta transacções. Alternativamente pode-se recorrer a um contentor de ‘EJBs’ que garanta a transacção ao nível da ‘Framework’.
Para a tecnologia .Net a minha preferência resume-se à utilização do ‘Distribucted Transaction Manager’, e efectuar manualmente a gestão das transacções.

Em primeiro lugar é necessário encapsular a ligação à base de dados utilizada, desta forma ficaremos com a classe:


public class Database implements IDatabase
{
private Connection connection;
private boolean transactionStarted = false;

public Database(String databaseName, String username, String password)
throws ClassNotFoundException, SQLException
{
Class.forName("org.gjt.mm.mysql.Driver");
connection = DriverManager.getConnection(...);
}


A nossa classe irá implementar um Interface que apresenta todos os seus métodos públicos, e a ligação à base de dados é feita no construtor. A forma como a ligação é feita é irrelevante, e pode-se optar por ler os dados da ligação de um ficheiro de configuração.


public Connection getConnection()
{
return connection;
}

public void beginTransaction()
throws TransactionAlreadyStartedException, SQLException
{
if (transactionStarted)
throw new TransactionAlreadyStartedException();
setTransactionStarted(true);
connection.setAutoCommit(false);
}

public void commitTransaction()
throws TransactionNotStartedException, SQLException
{
if (!transactionStarted)
throw new TransactionNotStartedException();
connection.commit();
connection.setAutoCommit(true);
setTransactionStarted(false);
}

public void rollbackTransaction()
throws TransactionNotStartedException, SQLException
{
if (!transactionStarted)
throw new TransactionNotStartedException();
connection.rollback();
connection.setAutoCommit(true);
setTransactionStarted(false);
}

public boolean inTransaction()
{
return transactionStarted;
}


O método getConnection() retorna a ligação que se está a utilizar, mas é importante garantir que o nosso código não vai executar métodos que afectem o estado das transacções. Eu optei por esta forma por questões de simplicidade, mas se quisermos ser rigorosos teremos que utilizar uma técnica em que a ligação não seja exposta publicamente.



protected void setTransactionStarted(boolean transactionStarted)
{
this.transactionStarted = transactionStarted;
}
}


Este método será utilizado para se iniciar uma nova transacção.

Em seguida iremos criar uma 'Factory', por duas razões. Em primeiro lugar queremos garantir que apenas existirá um objecto do tipo Database no nosso programa, depois porque mais tarde iremos substituir este objecto por outro sem que o nosso programa se aperceba disso.


public class DatabaseFactory implements IDatabaseFactory
{
private IDatabase database;
private ILog log;

public ModelFactory() throws ClassNotFoundException, SQLException
{
}

public setDatabase(IDatabase database)
{
this.database = database;
}

public IDatabase getDatabase()
{
return database;
}
}


Esta classe é bastante simples, e penso que dispensa comentários.

Agora falta arranjar uma forma que permita ao nosso programa localizar a factory disponível, por forma a obter a ligação. Eu optei pelo padrão 'Service Locator', mas existem outras técnicas válidas.


public final class Services {

private static IModelFactory modelFactory;

private Services() {
}

public static IModelFactory geModelFactory()
{
return modelFactory;
}

public static void setModelFactory(IModelFactory factory)
{
modelFactory = factory;
}
}


No ponto de arranque do nosso programa procede-se à configuração da classe Services, para podermos disponibilizar as fábricas que serão utilizadas durante a execuçãp. Neste exemplo apenas temos uma 'Factory' mas é comum existir pelo menos uma por camada.

Agora que temos o nosso ambiente de desenvolvimento completo, podemos começar a trabalhar uma classe que irá aceder aos dados. No entanto, à boa maneira ágil, iremos começar pelos testes :)
O nosso objectivo é registar um cliente e a sua morada, estando estes dados em tabelas diferentes (o exemplo poderia ser melhor, mas estou com falta de imaginação). Por questões de simplicidade o cliente é apenas composto pelo seu nome, a morada pela localidade e não irei utilizar sequências nas tabelas (o nome é a chave).

Nos testes iremos substituir a classe Database por uma outra cuja transacção não seja efectuada. Assim teremos uma classe de nome DatabaseTestInstance para ser utilizada exclusivamente nos testes.

   
public class DatabaseTestInstance extends Database implements IDatabase
{
public Database(String databaseName, String username, String password)
throws ClassNotFoundException, SQLException
{
super(databaseName, username, password);
getConnection().setAutoCommit(false);
}

public void commitTransaction()
throws SQLException, TransactionNotStartedException
{
rollBackTransaction();
}

public void rollBackTransaction()
throws SQLException, TransactionNotStartedException
{
super.rollbackTransaction();
getConnection().setAutoCommit(false);
}
}


Como podemos observar, o contrutor desta classe desliga o modo 'auto-commit' e faz sempre 'rollback' quando uma transacção termina.

Passando ao teste propriamente dito temos:


public class ClientTest extends TestCase
{
private IClientModel clientModel;

public TestBase(String name) throws ClassNotFoundException, SQLException
{
super(name);
IDatabaseFactory databaseFactory = new DatabaseFactory();
modelFactory.setDatabase(
new model.tests.unit.injections.Database
("database", "user", "pass"));
Services.setModelFactory(databaseFactory);
}


Aqui pode-se observar que estamos a substituir a classe que representa a ligação por uma classe de testes. A este conceito dá-se o nome de 'Dependency Injection'.


public static junit.framework.Test suite()
{
junit.framework.TestSuite suite =
new junit.framework.TestSuite(MillionaireTest.class);
return suite;
}

protected void setUp() throws java.lang.Exception {
super.setUp();
clientModel = new ClientModel();
}

public void testInsertClient() throws SQLException {
clientModel.InsertClient("Maria", "Lisboa");

IDatabase database = Services.getDatabaseFactory().getDatabase();
Statement statement = database.getConnection().createStatement();
String query = "SELECT count(*) as count " +
"FROM clients, local" +
"WHERE name like 'Maria'";
statement.execute(query);
ResultSet result = statement.getResultSet();
result.next();

assertTrue("Client not inserted.", result.getInt("count") == 1);

result.close();
}


Este teste verifica a inserção de um novo cliente. O próximo testa se o número de clientes na tabela é o esperado.


public void testCountClients() throws SQLException {
assertIsTrue("Number of clients is not the expected",
clientModel.countClients() == 10);
}


Desde já pode-se verificar que se o isolamento entre os testes não se verificar, correr testInsertClient e depois testCountClients é diferente de correr testCountClients e depois testInsertClient.

Agora que temos os testes, falta apenas programar a classe ClientModel:


public class ClientModel implements IClientModel
{

private final String GET_NUMBER_CLIENTS = "SELECT count(*) as count " +
"FROM clients";

private final String INSERT_CLIENT = "INSERT INTO clients VALUES(?)";

private final String INSERT_LOCAL = "INSERT INTO local VALUES(?, ?)";

public ClientModel()
{
}

public int countClients() throws SQLException
{
IDatabase database = Services.getDatabaseFactory().getDatabase();
Statement statement = database.getConnection().createStatement();

statement.execute(GET_NUMBER_CLIENTS);
ResultSet result = statement.getResultSet();
result.next();

int numberOfClients = result.getInt("count");
result.close();

return numberOfClients;
}


Note-se a subtileza da declaração da variável 'database'. O valor atribuído a esta variável é o que a fábrica retornar, e é isso que torna possível substituir a ligação à base de dados sem que o programa tenha conhecimento.

Agora o método em falta:


public void insertClient(String clientName, String local)
throws SQLException, InvalidValueException, ModelException
{
IDatabase database = Services.getDatabaseFactory().getDatabase();
Statement statement = database.getConnection().createStatement();

try {
db.beginTransaction();
insertClient(clientName);
insertLocal(local, clientName);
db.commitTransaction();
}
catch (TransactionException e)
{
throw new ModelException(e);
}
}
...


Deixo por implementar os métodos insertClient e insertLocal. De qualquer forma, é possível verificar que o comportamento de db.commitTransaction() é diferente caso se esteja em testes ou a executar o programa.


Resumo:
Verificámos como é possível garantir o isolamento dos testes unitários no que diz respeito à utilização de um modelo de dados. Para isso tivemos que encapsular a ligação numa classe, utilizar uma fábrica e injectar as dependências através de um 'service locator'.
Gostaria ainda de realçar que esta técnica funciona se o nosso programa não efectuar vários pedidos ao DBMS em simultâneo (i.e. não efectuar transacções concorrentes). Além disso, se acima da camada de acesso aos dados necessitar de iniciar transacções, esta técnica deve ser aplicada nessa camada também. Para evitar problemas futuros, é aconselhável começar transacções sempre na mesma camada.

Um abraço,
Gama Franco