Skip to content

(Cobertura 100%) != (100% Testado)

O assunto hoje é teste unitário !!

Quase sempre que converso com uma equipe de desenvolvimento, chega um determinado ponto da conversa em que o assunto é cobertura de teste unitário. Nesse momento, 80% das vezes me irrito ao perceber que a mente das pessoas está totalmente voltada para a mecânica da “coisa”: Obter 100% de cobertura nos testes através de uma ferramenta de apoio, como é o caso da Emma. Ai vem sempre meu discurso: “Galera, o importante não é obter 100% de código fonte “verdinho” (sinônimo de código coberto no Emma). O importante é cobrir todos os cenários que merecem ser testados, que possuam uma lógica que, se alterada, vai FERRAR com o sistema”. O que mais me irrita ainda, é que demora para as pessoas entenderem o que eu estou falando, as vezes tenho até que mostrar um caso de teste que comprove minhas palavras.

Para aqueles que estejam pensando que sou contra o uso de ferramentas para verificação de cobertura dos testes, por favor, não pensem. Sou totalmente a favor das mesmas. No cenário atual, onde a minoria das equipes pensam no simples teste unitário, fico feliz quando vejo alguma equipe buscando cobertura de código testado.

Meu ponto é que, muitas vezes, as pessoas ficam bitoladas na cobertura 100%, testando até métodos anêmicos e sem importância. Porém, só por verem a ferramenta acusando 100% de cobertura, as pessoas simplesmente param de raciocinar e deixam passar casos de teste que são de suma importância.

Bom, para ilustrar este cenário, vou dar um exemplo “bobo”, mas que reflete bem aquilo que disse acima.
Imagine o seguinte código e sua classe de teste, ilustrados nas figuras abaixo:

Figura 1 - Código a ser testado
Figura 2 - Código de teste

Quando acionado o plugin do Emma nesta classe de teste, temos a seguinte resposta:

Figura 3 - Indicativo de cobertura

Sim, 100% do código coberto. Óbvio, construímos dois casos de teste, sendo que cada um deles entra em um dos blocos condicionais. Entretanto, cade o teste que simula o fluxo quando os dois blocos são percorridos ? Exatamente, isso não está testado, e é de suma importância neste caso.

Imagine, por exemplo, que por um incidente, alguém esteja visualizando a classe ChiesMath e, sem querer, altera a regra do segundo bloco condicional para:

Figura 3 - Condição modificada

Executemos os testes unitários novamente e ?? Todos os testes passam e 100% de cobertura é acusado. Porém, se a entrada do método for, por exemplo, 14, o score final será 10, diferente do que seria retornado no código original, 15.
Veja ai o problema, a lógica do método foi modificada e nada de os testes unitários pegarem o erro.
Se existisse o teste unitário que simulasse o fluxo que passa por ambos blocos condicionais, certamente o teste iria quebrar ao ser executado contra a versão alterada.

Bom, é isso. Cobertura de teste É SIM legal e util. Mas por favor, não se restrinja ao label de 100% de cobertura.

Spring com JBoss 5 – Problema com o Scan de recursos

O framework spring possui um mecanismo de scan para a detecção de recursos durante a inicialização do servidor de aplicação. Entrando em mais detalhes, o componente utilizado para este scan assume que os recursos estão em diretórios ou arquivos JAR.
O problema é que, o JBoss, a partir de sua versão 5.0, utiliza o conceito de Virtual File System (VFS) no classpath. Desta forma, ao tentar fazer o scan, o spring acusa erros similares à stack abaixo:

ERROR org.springframework.web.servlet.DispatcherServlet - Context initialization failed org.springframework.beans.factory.BeanDefinitionStoreException:
I/O failure during classpath scanning; nested exception is java.util.zip.ZipException: error in opening zip file  
at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(ClassPathScanningCandidateComponentProvider.java:222)

A solução para este problema é fornecida através do framework snowdrop. Através deste, o JBoss disponibiliza uma classe que consegue fazer o scanning segundo a estrutura do VFS.
A biblioteca que contém esta classe é a snowdrop-vfs-XXX.jar, onde deve-se usar a versão 1.1 para spring 3 e a versão 1.0 para spring 2.5. Ainda, o download desta biblioteca pode ser realizado em http://www.jboss.org/snowdrop/downloads, bastanto inclui-la no WEB-INF/lib da aplicação.

Por fim, após incluir esta lib dentro da aplicação, é necessário informar ao spring que é para ser utilizada a classe VFSXmlWebApplicationContext no momento do scan. Para tanto, basta adiconar um context-param no web.xml, conforme listagem abaixo. Ressaltando que a classe informada nesta listagem está contida na lib do snowdrop.


<context-param>
<param-name>contextClass</param-name>
<param-value> org.jboss.spring.vfs.context.VFSXmlWebApplicationContext</param-value>
</context-param>

Qualquer dúvida estou a disposição.
[]’s

jBPM – couldn’t get task instances by ids

Passei um bom tempo pesquisando sobre este erro, então resolvi postar aqui o veredito.

Este problema é referente ao uso da variável #{taskInstanceList} do jBPM, responsável por trazer todas as tarefas que estão associadas a um determinado ator. O inconveniente acontece quando esta variável é chamada, por exemplo dentro de um código feito com seam (meu caso), e o ator em questão não possui nenhuma tarefa passível a ser executada por ele. Assim, é gerada a exceção:

org.jbpm.persistence.JbpmPersistenceException: couldn't get task instances by ids '[]'
    at org.jbpm.db.TaskMgmtSession.findTaskInstancesByIds(TaskMgmtSession.java:204)
    at org.jbpm.db.TaskMgmtSession.findPooledTaskInstances(TaskMgmtSession.java:123)
Caused by: org.hibernate.hql.ast.QuerySyntaxException: unexpected end of subtree [

      select ti
      from org.jbpm.taskmgmt.exe.TaskInstance ti
      where ti.id in (  )

  ]

Para quem quer economizar leitura, a solução é: migre seu jBPM para uma versão igual ou superior à 3.2.9.

 

O erro acontece porque, no cenário descrito, acontece a execução de uma query que recebe uma lista vazia dentro da cláusula WHERE IN. A classe que contém a chamada desta query é a org.jbpm.db.TaskMgmtSession. Através do trecho de código abaixo, repare que, em primeira instância, acontece a busca de todas as pooledTasks passíveis de execução pelo ator em questão, conforme o método findPooledTaskInstances. Verifique que, logo após, é chamado o método findTasksInstancesByIds, passando como parâmetro o resultado da busca anterior.

public List findPooledTaskInstances(String actorId) {
    try {
      List taskInstanceIds = session.getNamedQuery(“TaskMgmtSession.findPooledTaskInstancesByActorId”)
          .setString(“actorId”, actorId)
         .list();
      return findTaskInstancesByIds(taskInstanceIds);
         ….
public List findTaskInstancesByIds(List taskInstanceIds) {
    try {
      return session.getNamedQuery(“TaskMgmtSession.findTaskInstancesByIds”)
          .setParameterList(“taskInstanceIds”, taskInstanceIds)
          .list();
    }

….

Como, no cenário de erro, a primeira busca retorna um lista vazia (não há tarefas que o ator possa executar), a segunda recebe uma lista vazia dentro da cláusula WHERE, segundo o próprio log nos mostra.
 select ti
      from org.jbpm.taskmgmt.exe.TaskInstance ti
      where ti.id in (  )

Este erro foi reportado como um bug, disponível em https://issues.jboss.org/browse/JBPM-2637. Aparentemente, tudo funcionava bem quando, nas versões 3.2.7 e 3.2.8, foi removida uma validação que cobria o caso de a lista passada para a query estar vazia. Deste modo, como a própria resolução do bug descreve, faça a atualização do jBPM para a versão 3.2.9, onde a validação foi novamente codificada.

 

Cozinha Italiana

Aleatoriedade da vez: Cozinha Italiana.

Quem já me conhece sabe da fascinação que tenho por coisas Italianas (culinária, paisagens, modo de viver, futebol, entre outras). Junte isso a minha paixão por cozinhar, e chegue ao resultado de que sempre que posso estou na cozinha tentando preparar e sentir o sabor de algum prato italiano, de preferência massas.
Este domingo tirei algumas horas do dia para por em prática esse meu hobby. Fui até meu livro de receitas do Jamie Oliver, “a itália de jamie”, e decidi fazer a famosa pasta al forno com pomodori e mozzarella. Duas horinhas tomando um bom vinho e voilà …

Indico fortemente este prato, que junta a simplicidade da cozinha italiana, com o gosto de tomates frescos e o aroma sem igual do manjericão. Pra quem quiser se arriscar, podem seguir este link: http://www.jamieoliver.com/recipes/pasta-recipes/baked-pasta-with-tomatoes-mozzarella.

Aproveitando a deixa, já que estou falando de Itália, a Folha está lançando uma coleção de exemplares sobre a culinária de várias regiões italianas. Os livros são lançados todos os domingos, sendo que já estão vendendo o quarto  da coleção. Comprei todos esta manhã e também recomendo fortemente pra quem gosta deste tipo de leitura e receitas.

Por isso é hoje, o próximo post já será técnico novamente. :o)

[]’s

Um simples exemplo usando JBoss Messaging com Mysql

Neste post pretendo passar um exemplo bem básico do uso de filas JMS no JBoss Messaging utilizando persistência em um banco de dados relacional, no meu caso mysql. As configurações que aqui serão demonstradas foram executadas em um ambiente com a versão 5.1 do JBoss AS, utilizando como base o perfill all, que já vem configurado com o módulo de mensageria.

Primeiramente vamos à definição de nossa fila, editando o arquivo …/deploy/messaging/destinations-service.xml. Adicione o trecho abaixo após as configurações das filas DLQ e Expiry, que já veem configuradas por padrão. Este mbean representa a descrição de nossa fila, que será referenciada no código fonte.

 <mbean code="org.jboss.jms.server.destination.QueueService"
          name="jboss.messaging.destination:service=Queue,name=ChiesQueue"
          xmbean-dd="xmdesc/Queue-xmbean.xml">
      <depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer</depends>
      <depends>jboss.messaging:service=PostOffice</depends>
      <attribute name="JNDIName">queue/ChiesQueue</attribute>
      <attribute name="RedeliveryDelay">10000</attribute>
      <attribute name="MaxDeliveryAttempts">3</attribute>
 </mbean>

Agora vamos à configuração do uso da persistência em um banco de dados mysql.
Como padrão, o serviço de mensagens vem configurado para utilização de hsql através da presença do arquivo hsqldb-persistence-service.xml, que também está contido no diretório …/deploy/messaging/. Para utilização do mysql basta remover este arquivo (ou renomeá-lo como hsqldb-persistence-service.xml.rej) e substituí-lo por um persistence service do mysql. Um exemplo desse arquivo pode ser encontrado em …/jboss/docs/examples/jms/mysql-persistence-service.xml. Podemos simplesmente copiar esse exemplo e substituir as referências para o datasource com o jndiname utilizado no DS de configuração do banco de dados a ser criado. No meu caso, criei um schema no mysql chamado jmsspike e em seguida o jmsspike-ds.xml como segue abaixo:

<datasources>
  <local-tx-datasource>
    <jndi-name>JMSSpikeDS</jndi-name>
    <connection-url>jdbc:mysql://localhost:3306/jmsspike</connection-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
    <user-namevspikes</user-name>
    <password>spikes01</password>
    <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
    <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
    <valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLValidConnectionChecker</valid-connection-checker-class-name>
    <metadata>
       <type-mapping>mySQL</type-mapping>
    </metadata>
  </local-tx-datasource>
</datasources>

Abaixo segue os pontos do arquivo mysql-persistence-service.xml que devem ser alterados. Atente ao fato de que o par de alterações abaixo são aplicados em três locais no arquivo.

De: jboss.jca:service=DataSourceBinding,name=DefaultDS
Para: jboss.jca:service=DataSourceBinding,name=JMSSpikeDS

De: java:/DefaultDS
Para: java:/JMSSpikeDS

Vale a pena destacar que este arquivo possui todos os statements nescessários para a inicialização da base de dados com as tabelas utilizadas pelo serviço. Caso sua base/usuário tenha privilégios de criar tabelas, basta conservar os atributos CreateTablesOnStartup como true que, quando o servidor for inicializado pela primeira vez, toda estrutura inicial será criada. Caso não exista esse privilégio, apenas copie os statements que existem neste arquivo e execute-os manualmente.

Um último passo para esta troca do hsql para mysql é a alteração do arquivo messaging-jboss-beans.xml, substituindo a referência ao datasource DefaultDS para o nosso JMSSpikeDS, como segue no trecho abaixo:

De: java:/DefaultDS
Para: java:/JMSSpikeDS

Pronto. Toda a configuração do servidor está pronta para abrigar nossa aplicação de exemplo.

A definição da fila no código fonte é dada pelo trecho de código abaixo:

@MessageDriven(
		name="ChiesQueue",
		activationConfig= {  
				@ActivationConfigProperty(propertyName="destination",propertyValue="queue/ChiesQueue"),  
				@ActivationConfigProperty(propertyName="destinationType",propertyValue="javax.jms.Queue")  
		}	
)
public class SampleMDB implements MessageListener{

	@Override
	public void onMessage(Message message) {
		try {
			ObjectMessage objectMessage = (ObjectMessage) message;
			SampleMessage sampleMessage = (SampleMessage) objectMessage.getObject();
			System.out.println("Msg received on Queue: sequential = " + sampleMessage.getSequential() + " description = " + sampleMessage.getDescription());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

Já o código que insere mensagens nesta fila pode ser verificado abaixo:


InitialContext ctx = new InitialContext();
Queue queue = (Queue) ctx.lookup("queue/ChiesQueue");
QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup("ConnectionFactory");
QueueConnection connection = factory.createQueueConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
ObjectMessage message = session.createObjectMessage(sampleMessage);
MessageProducer producer = session.createProducer(queue);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
producer.send(message);

No meu caso, para fins de teste, coloquei o código do producer em um recurso REST. Porém, não serão aqui detalhadas as implementações que não estão diretamente relacionadas à configuração JMS. Caso queiram se basear na minha implementação, segue abaixo o link para o github.

https://github.com/rafachies/jmsspike

Bom, espero ter sido claro nas explicações. Qualquer dúvida estou a disposição.

[]’s

Eclipse e sua saída para internet usando proxy

Recentemente passei por dificuldades para configurar meu Eclipse em um abiente que possuia um proxy para saída de informações para internet. Mesmo colocando todas informações do proxy em “Window/Preferences/General/Network Connection”, o Eclipse não tinha sucesso ao acessar o mundo externo, como por exemplo na instalação de um novo plugin.

Revirando alguns blogs achei um workaround bem útil, e que resolveu inteiramente meu problema. Este trata-se de incluir, no arquivo de configurações eclipse.ini, o parâmetro especificado abaixo:

-Dorg.eclipse.ecf.provider.filetransfer.excludeContributors = org.eclipse.ecf.provider.filetransfer.httpclient

Este parâmetro, basicamente, diz que o Eclipse pode acessar o mundo externo através do HTTP Client padrão do sistema. Assim, tendo seu sistema, no meu caso o Fedora, configurado para utilizar o proxy em questão, tudo deve funcionar corretamente. Ainda, configure também o proxy no eclipse (“Window/Preferences/General/Network Connection”) em Manual, ao menos foi assim que funcionou em minha máquina.

Qualquer dúvida estou a disposição.

[]’s

RHQ 4.0.0 Released

Fantástico !!!

Ontem (03/05), através dos http://planet.jboss.org, recebi a notícia da versão 4.0.0 do RHQ, também conhecido na comunidade como JOPR.

Dentre as melhorias empregadas nessa nova versão, sem sobras de dúvidas a que mais se destaca é a referente a interface gráfica. Eu, particulamente, achava a parte gráfica o recurso mais carente da ferramenta. Porém, com a nova interface baseada no GWT com Ajax WEB 2.0 acredito que esta carência foi totalmene sanada. Vale a pena conferir !

O servidor está disponível em http://sourceforge.net/projects/rhq/files/rhq/rhq-4.0.0/, sendo que após a instação basta fazer o download do agente dentro da própria aplicação.

Bons testes a todos !

Coding Horror

Um prato de informações tecnológicas com uma pitada de conhecimento aleatório.

InfoQ

Um prato de informações tecnológicas com uma pitada de conhecimento aleatório.

JBoss Developer Recent Posts

Um prato de informações tecnológicas com uma pitada de conhecimento aleatório.

JBossDivers

Mergulhando no Mundo JBoss