Lançamento dos Cursos de Java da TriadWorks

Demorou, mas a hora chegou! É com muito prazer que anuncio o lançamento dos cursos de Java da TriadWorks. Todos os cursos serão presenciais e acontecerão em Fortaleza-Ceará.

Através do nosso site você confere todos os detalhes de cada curso, como conteúdo programático, valores, formas de pagamento e o calendário com as primeiras turmas. Falando em primeiras turmas, todas elas terão um desconto imperdível de lançamento: 20% de desconto!

Neste momento estamos ofertando 4 cursos, com turmas no período da noite e turmas aos sábados. Todos os cursos incluem apostila atualizada, didática exclusiva e instrutores qualificados com anos de experiência no mercado. Os cursos são:

As vagas são limitadas, por isso garanta já a sua! Faça a matrícula pelo Paypal e pague em até 6x sem juros – não existe desculpa para não se matricular!

Dúvidas, reservas ou informações dos cursos para você ou para sua empresa, entre em contato conosco através do formulário de contato no site ou envie um email diretamente para cursos@triadworks.com.br .

Fique por dentro das próximas turmas, descontos, promoções e novidades através da nossa página no Facebook ou Twitter. Acesse agora o site e confira os cursos em http://cursos.triadworks.com.br/ .

Tagged with: , , , , , , , , , , , , , , , ,
Posted in Desenvolvimento de Software, Hibernate, Java, JEE, JPA, JSF, JSF2, mercado, Spring, web

Testes isolados com jUnit Rules

Sempre houve uma discussão na comunidade sobre o que difere um teste de unidade de um teste de integração. Muitos consideram que um teste de unidade pode tocar o sistema de arquivos ou banco de dados; outros que um teste de unidade não deve levar mais do que 1 segundo para rodar; já outros consideram que testes de integração são testes que sempre tocam o banco de dados.

Alguns autores já tentaram definir o que um teste deve ter para ser considerado um teste de unidade. Outros autores, no entanto, foram mais além e tentaram definir o oposto, ou seja, o que um teste de unidade não é. Michael Feathers, por exemplo, listou 5 características que um teste de unidade não deve ter. Basicamente o que ele diz é que um teste não é um teste de unidade se:

  • Ele conversa com o banco de dados;
  • Ele se comunica através da rede;
  • Ele toca o sistema de arquivos;
  • Se ele não pode ser executado ao mesmo tempo com outros testes de unidade;
  • Se você tiver que configurar ou preparar o ambiente (como editar arquivos de configuração) para executar o teste;

Seguir os preceitos acima traz diversas vantagens na hora de escrever suas classes de teste, como testes de unidade mais rápidos, melhor feedback e rastreabilidade em caso de erros e claro, testes isolados e independentes.

Um teste de unidade só deveria ter um motivo para falhar, e este motivo é sua validação, seu assert. Quanto menos isolado um teste é, maiores são suas chances de falhar, e pior, por motivos algumas vezes difíceis de determinar.

Um teste de unidade deve rodar de forma isolada do ambiente externo e dos outros testes, isto é, o que um teste de unidade faz ou deixa de fazer não deveria influenciar na assertividade dos testes que executarão depois dele. Ter testes de unidade interdependentes é um problema e uma má prática.

Um forte indício para avaliar se seus testes de unidade foram escritos de maneira isolada é se a ordem em que eles rodam influencia o resultado final da bateria. Se os testes precisam ser executados em uma ordem específica para passarem, provavelmente, eles não estão bem isolados e independentes.

Se sujou, limpe

Mesmo testes de unidade aparentemente simples podem sofrer influências externas se não tomarmos os devidos cuidados. Um teste inofensivo para uma classe que formata uma data é um bom exemplo disso:

public class FormatadorDeData {

	public String formata(Date data) {
		SimpleDateFormat sdf  = new SimpleDateFormat("dd 'de' MMMM");
		return sdf.format(data);
	}
}

E sua classe de teste para verificar seu comportamento:

public class FormatadorDeDataTest {

	@Test
	public void deveFormatarDataParaRelatorio() {

		FormatadorDeData formatador = new FormatadorDeData();
		String dataFormatada = formatador.formata(data("23/12/2013"));

		assertEquals("23 de Dezembro", dataFormatada);
	}

	// outros métodos de teste
}

Se o desenvolvedor que escreveu o teste acima rodá-lo em um ambiente com a localização “pt_BR” o resultado será o esperado e o teste passará. Contudo, ao integrar o código no servidor de integração contínua (CI), que roda em outra localização, como “en_US”, o teste quebrará sem dúvida.

O problema é que a classe SimpleDateFormat usa o Locale padrão da JVM quando não informamos qual iremos utilizar. Como a JVM usa o locale do OS então temos uma dependência com o ambiente externo, pois o teste só passaria se rodassemos ele em um ambiente pré-configurado.

Levando em conta que nosso código não precisa ser internacionalizável, a correção é simples, basta informar o Locale “pt_BR” ao instanciar a classe SimpleDateFormat.

public class FormatadorDeData {
	public String formata(Date data) {
		SimpleDateFormat sdf  = new SimpleDateFormat("dd 'de' MMMM", new Locale("pt", "BR"));
		return sdf.format(data);
	}
}

Sabemos que apenas corrigir o problema não é suficiente, precisamos garantir que ele não volte a acontecer. E conseguimos isso escrevendo mais testes para nossa classe de produção. Podemos melhorar nossa classe de testes com o uso da anotação @Before do jUnit.

public class FormatadorDeDataTest {

	@Before
	public void setup() {
		Locale.setDefault(new Locale("en", "US"));
	}

	@Test
	public void deveFormatarDataParaRelatorio() {

		FormatadorDeData formatador = new FormatadorDeData();
		String dataFormatada = formatador.formata(data("23/12/2013"));

		assertEquals("23 de Dezembro", dataFormatada);
	}

	// outros métodos de teste
}

O método com @Before irá executar antes de cada método de teste (@Test), ou seja, todos os métodos de teste da classe executarão com o locale “en_US”, se todos os métodos passarem então temos certeza que a JVM não interfere na execução da classe FormatadorDeData.

Resolvemos um problema, mas acabamos criando outro. Alteramos o locale da JVM, que é uma configuração global, e todas as outras classes de teste rodarão com o locale que definimos. A priori isto não parece um problema, mas poderá quebrar outros testes que não esperavam por isso. É uma sujeira que deixamos no caminho e precisamos limpa-la.

Para que um teste rode de forma isolada, ele deve iniciar sempre em um estado limpo e válido, e com seu término, ele deve sempre desfazer qualquer sujeira que ele tenha deixado no caminho. A sujeira pode ser desde uma variável de ambiente ou da JVM, um arquivo ou diretório no sistema de arquivos, recursos abertos do OS, entre outros.

Novamente, o jUnit nos ajuda com a anotação @After, que é análoga ao @Before. Com ela podemos voltar o locale da JVM para seu estado inicial ao fim de cada teste. Teríamos um código semelhante a este:

public class FormatadorDeDataTest {

	private static final Locale localeOriginal = Locale.getDefault();

	@Before
	public void setup() {
		Locale.setDefault(new Locale("en", "US"));
	}

	@After
	public void cleanUp() {
		Locale.setDefault(localeOriginal);
	}

	// todo os métodos de teste
}

A solução acima é bastante simples, porém seu reuso é tão restrito quanto sua simplicidade. Para reusa-la noutras classes teríamos que repetir o código – e já sabemos que repetição de código nunca é uma coisa boa.

jUnit Rules

A partir do jUnit 4.7 foi adicionado um conceito chamado Rules, ele é muito semelhante aos runners customizados (@RunWith), porém são mais simples, mais flexíveis e menos limitados. A grosso modo, você usa a anotação @Rule em uma classe para indicar que deseja executar algum comportamento antes ou depois de cada método de teste (@Test), muito semelhante a um interceptor.

Para escrevermos nossa própria Rule, basta criarmos uma classe que implemente a interface TestRule. Ela possui um contrato bem simples:

public interface TestRule {
	public Statement apply(Statement base, Description description);
}

Tomando o problema do locale anterior, poderíamos resolvê-lo da seguinte forma:

import java.util.Locale;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class LocaleRule implements TestRule {

	private static final Locale defaultLocale = Locale.getDefault();
	private final Locale locale;

	public LocaleRule(Locale locale) {
		this.locale = locale;
	}

	@Override
	public Statement apply(final Statement base, Description description) {
		return new Statement() {
			@Override
			public void evaluate() throws Throwable {
				Locale.setDefault(locale); // altera o locale padrão
				try {
					base.evaluate(); // executa o teste
				} finally {
					Locale.setDefault(defaultLocale); // volta o locale inicial
				}
			}
		};
	}
}

Nossa classe de teste só precisaria declarar um atributo público e anota-lo com @Rule. A classe de teste teria o código abaixo:

import org.junit.Rule;

public class FormatadorDeDataTest {

	@Rule
	public LocaleRule locale = new LocaleRule(new Locale("en", "US"));

	@Test
	public void deveFormatarDataParaRelatorio() {

		FormatadorDeData formatador = new FormatadorDeData();
		String dataFormatada = formatador.formata(data("23/12/2013"));

		assertEquals("23 de Dezembro", dataFormatada);
	}

	// outros métodos de teste
}

O código além de mais simples e limpo, nos permite reusar a classe LocaleRule em outros testes facilmente. O código pode ainda ser melhorado se estendermos a classe ExternalResource, na qual foi desenhada para iniciar e terminar recursos externos relacionados a sistema de arquivos, sockets, conexões de banco etc.

Já existem algumas Rules prontas que vem com jUnit 4.10. Como por exemplo, rule para criar (e deletar) arquivos e diretórios temporários, para tratamento mais elegante de exceptions, para timeout, entre outras. Além disso, é possível rodar várias Rules juntas na mesma classe de teste, o que nos dá maior flexibilidade e reuso de código - algo que não é possível com @RunWith.

A funcionalidade Rules do jUnit é muito poderosa, ela nos permite executar cada método de teste dentro de um contexto isolado, dentro de sua própria sandbox. Com ela é possível criar contextos do Spring, preparar o EntityManager da JPA ou uma conexão JDBC para os testes de integração, fazer mocking e injeção de dependência (DI), levantar servidores embarcados e muitos mais.

Concluindo

Existem diversas práticas para manter os testes de unidade isolados e independentes, que vão desde o uso de mocks e stubs, preparar objetos e variáveis globais até o controle de onde arquivos e diretórios são criados. O ideal é escrevermos cada método de teste como se ele rodasse em seu próprio contexto, sua própria sandbox, dessa forma evitamos a interdependência entre os testes.

Cuidar do código de teste é tão importante quanto cuidar do código de produção, com jUnit Rules podemos escrever testes mais simples, menos fragéis e ainda por cima ganharmos reusabilidade de código.

Testes mal isolados trazem problemas que levam bastante tempo para resolver, pois nem sempre é fácil detectar a causa do problema, já que na maioria das vezes o teste passa quando executado sozinho, mas quebra quando executado junto com a bateria em uma máquina especifica e, até quem sabe, em uma determinada data ou horário.

Se cada desenvolvedor da equipe se preocupar em isolar a classe de teste que ele escreve adequadamente, limpando o que o teste suja, com certeza a bateria de testes do projeto rodará verde por muito mais tempo.

E aí, você já teve algum problema com testes interdependentes? Já conhecia ou já usou o jUnit Rules?

Tagged with: , , , , , , , , , , , , , , , ,
Posted in Boas Práticas, Desenvolvimento Ágil, Java, Testes automatizados

Hibernate Efetivo: Logs

Hibernate Efetivo: Logs

É uma prática muito comum quando se está aprendendo ou desenvolvendo com Hibernate exibir o SQL gerado pelo framework a cada consulta ou atualização no banco de dados. Existem inúmeros motivos para se habilitar os logs do Hibernate, mas independente do motivo, é sempre uma boa prática habilita-lo durante o desenvolvimento.

Através dos logs é possível encontrar bugs e gargalos em consultas, validar mapeamentos das entidades, determinar o número de queries para completar uma tarefa do ORM, sem falar que ver o SQL é importante para otimizar as queries através de indíces e configurações específicas do banco de dados.

Não é difícil descobrir que para habilitar os logs do SQL basta uma pequena configuração na SessionFactory do Hibernate através do arquivo hibernate.cfg.xml ou, se você estiver usando JPA, do persistence.xml:



	
		
		true
	

A saída no console seria semelhante ao SQL abaixo, o que não ajuda muito na hora de ler e entender o código:

Hibernate: select conta0_.ID as ID6_0_, conta0_.DESCRICAO as DESCRICAO6_0_, conta0_.FRANQUIA_ID as 
FRANQUIA6_6_0_, conta0_.NOME as NOME6_0_, conta0_.SALDO_INICIAL as SALDO4_6_0_, conta0_.tipo as tipo6_0_ 
from CONTA conta0_ where conta0_.ID=?

Indo um pouco mais longe, ainda é possível exibir o SQL formatado para facilitar a leitura das queries e até embutir comentários para saber exatamente o que o Hibernate está tentando fazer. Novamente, através de uma simples configuração:



	
		
		true
		true
		true
	

A saída no console melhora consideravelmente:

Hibernate:
/* load br.com.triadworks.financeiro.model.Conta */ select
        conta0_.ID as ID19_0_,
        conta0_.DESCRICAO as DESCRICAO19_0_,
        conta0_.FRANQUIA_ID as FRANQUIA6_19_0_,
        conta0_.NOME as NOME19_0_,
        conta0_.SALDO_INICIAL as SALDO4_19_0_,
        conta0_.tipo as tipo19_0_ 
    from
        CONTA conta0_ 
    where
        conta0_.ID=?

O problema é que habilitar os logs através da propriedade hibernate.show_sql=true é uma solução simples, rápida mas pouco eficaz. O SQL gerado é enviado diretamente para o System.out, o que é pouco flexível, pois todo o log ficará embaralhado com o restante dos logs das aplicações no servidor. Em desenvolvimento o problema não é tão aparente, mas em produção é onde percebemos sua limitação.

Uma solução mais apropriada para desenvolvimento e produção é utilizar um framework de logging. O Hibernate adotou o SLF4J como façade para frameworks de logging já faz um tempo, o que permite utilizar a maioria dos frameworks de mercado, como Log4j, LogBack entre outros.

Considerando que estamos usando o Log4j e que queremos exibir os logs no console, podemos configurar os logs do Hibernate no arquivo log4j.properties na raiz do classpath da sua aplicação:

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss} %5p [%-20c{1}] %m%n

log4j.rootLogger=warn, stdout

log4j.logger.org.hibernate.SQL=DEBUG  # É equivalente ao hibernate.show_sql=true

A principal vantagem de se habilitar os logs através do Log4j em vez da hibernate.show_sql é a sua flexibilidade. Com um framework de logging podemos, além de formatar e definir os níveis dos logs, direcionar a saída para onde quisermos, como o console, arquivos, sockets, enviar os logs por e-mail ou até mesmo todos de uma vez. Também é possível configurar a política para rotacionar os arquivos de logs, como quebrar os arquivos por data, tamanho e até remover arquivos antigos. Indo um pouco mais além, podemos ligar, desligar e reconfigurar os logs em runtime quando necessário, o que não possível com uso do hibernate.show_sql.

Outra dica ainda interessante, é que podemos logar os parâmetros que passamos para as queries, que ajuda bastante durante o debug. Quando utilizamos um framework de logging, no caso, o Log4j, isso se torna uma tarefa simples:

log4j.logger.org.hibernate.SQL=DEBUG  
log4j.logger.org.hibernate.type=TRACE   # Exibe os parâmetros JDBC

Certamente que isso ajuda muito em momentos de desespero para descobrir a causa de um bug na aplicação:

18:32:47 DEBUG [SQL                 ] 
    /* insert br.com.triadworks.financeiro.model.Conta
        */ insert 
        into
            CONTA
            (DESCRICAO, FRANQUIA_ID, NOME, SALDO_INICIAL, tipo) 
        values
            (?, ?, ?, ?, ?)
18:32:47 TRACE [BasicBinder         ] binding parameter [1] as [VARCHAR] - Conta nova
18:32:47 TRACE [BasicBinder         ] binding parameter [2] as [BIGINT] - 1
18:32:47 TRACE [BasicBinder         ] binding parameter [3] as [VARCHAR] - BANCO DO BRASIL
18:32:47 TRACE [BasicBinder         ] binding parameter [4] as [NUMERIC] - 5000.55
18:32:47 DEBUG [EnumType            ] Binding 'CONTA_POUPANCA' to parameter: 5

O time do Hibernate tem investido muito nas mensagens de log, tanto é que ele possui mensagens de logs bem detalhadas, legíveis e categorizadas para ajudar os desenvolvedores. É possível logar mensagens para vários de seus componentes, como instruções DDL, transações, cache de segundo nível, HQL, entre outras.

Desligue os logs em produção

Independente de qual estratégia você use para logar o SQL do Hibernate, elas devem ser utilizadas somente em desenvolvimento. É de suma importância desligar os logs do Hibernate em ambiente de produção.

Caso contrário, a aplicação irá logar um número excessivo de mensagens, o que pode gerar I/O desnecessário em produção e, consequentemente, problemas de performance. Sem falar na dificuldade para rastrear e identificar erros. Imagine, por exemplo, ter que encontrar um erro específico de negócio que ocorreu no final de semana passado em um arquivo de log com mais de 10gb de tamanho? Parece impossível de acontecer, mas é bastante comum, principalmente com o log de SQL do Hibernate habilitado.

Existem diversas estratégias para desligar os logs em produção, a mais simples, com o Log4j, é passar um argumento para JVM no startup da sua aplicação ou servidor, apontando para outro arquivo de configuração do Log4j localizado no classpath. Algo como:

-Dlog4j.configuration=log4j-producao.properties

Outra estratégia comumente usada, é substituir o arquivo log4j.properties durante o build do projeto, ou seja, ao gerar o .war da aplicação. Normalmente você terá um arquivo para cada ambiente, como produção, homologação e testes. O arquivo de desenvolvimento não difere muito do que vimos antes, já o de produção pode ser algo semelhante a este:

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss} %5p [%-20c{1}] %m%n

log4j.rootLogger=error, stdout, DAILY

# loga diariamente em um arquivo no formata myapp_errors.log.2013-12-10
log4j.appender.DAILY.Threshold=error
log4j.appender.DAILY=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DAILY.File=${catalina.base}/logs/myapp/myapp_errors.log
log4j.appender.DAILY.DatePattern='.'yyyy-MM-dd
log4j.appender.DAILY.layout=org.apache.log4j.PatternLayout
log4j.appender.DAILY.layout.conversionPattern=%d{HH:mm:ss} %5p [%-20c{1}] %m%n
log4j.appender.DAILY.Encoding=UTF-8

# hibernate
log4j.logger.org.hibernate=error
log4j.logger.org.hibernate.SQL=error
# outros frameworks
log4j.logger.org.springframework=error
log4j.logger.br.com.caelum.vraptor=error
# logs da app
log4j.logger.br.com.triadworks.financeiro=info

A configuração acima irá logar somente erros no console e em arquivos, além de rotacionar os arquivos de logs diariamente, ou seja, no final de cada dia será gerado um arquivo de log com o padrão myapp_errors.log.2013-12-10. Uma simples configuração deste tipo permite rastrear erros mais facilmente, pois além de o arquivo ser datado ele também possuirá um conteúdo muito menor.

Enfim, o modus operandi em produção é logar apenas o estritamente necessário, ou seja, erros e informações que possam ajudar na rastreabilidade e identificação de problemas.

Concluindo

Desligar e gerenciar de maneira simples os logs do Hibernate em produção é importante e traz melhorias de performance. Adotar e configurar bem um framework de logging em um projeto é mais importante ainda, pois certamente todos os frameworks no projeto permitem ajustes finos nos logs, o que pode facilitar absurdamente a entender e encontrar problemas em desenvolvimento e produção.

Tirar proveito destes frameworks é muito mais simples do que você pode imaginar e pode influenciar consideravelmente na performance da sua aplicação. No curso de Persistência com JPA 2 e Hibernate da TriadWorks é abordado vários detalhes e práticas na utilização do Hibernate, entre elas o uso efetivo dos logs.

E você, como está habilitando os logs do Hibernate na sua aplicação? Está lembrando de desligar os logs em produção?

Tagged with: , , , , , , , , , , , ,
Posted in Boas Práticas, Hibernate, Java, JPA