Posts Tagged ‘bug’

assertEquals para BigDecimal

October 22, 2008

Quando, desenvolvendo software, nos deparamos com alguma situação um tanto quanto bizarra, qual a primeira coisa a se fazer?
Sair correndo para perguntar naquele forum bacana certo? É uma alternativa.
Acho os foruns fantásticos, frequento bastante. São sem dúvida uma ótima fonte de estudo. Porém uma outra solução um pouco mais difícil, demorada, porém menos preguiçosa mais … digamos … nobre, seria perder alguns poucos minutos realizando uma pesquisa.

Hoje, desenvolvendo um “Pet Project” me deparei com uma situação dessas.
Veja o seguinte código:

import java.math.BigDecimal;

import org.junit.Test;
import static org.junit.Assert.*;

public class BigDecimalTest {

	private final BigDecimal n1 = new BigDecimal("13.23");
	private final BigDecimal n2 = new BigDecimal("13.27");

	@Test
	public void should_not_be_equals_using_junit_assert_method()
	{
		assertEquals(n1, n2);
	}

	@Test
	public void should_not_be_equals_using_BigDecimal_equals_method()
	{
		assertTrue(n1.equals(n2));
	}
}

Ambos deveriam falhar correto? Mas não foi o que aconteceu.
Inexplicavelmente o primeiro teste passou. Ficou verdinho! Isso mesmo.
Minha primeira idéia foi uma pesquisa rápida no google (ooohhh), mas não encontrei nada. Talvez não tenha procurado direito, é verdade. Achei mais interessante baixar o código fonte do JUnit e eis que encontro o seguinte trecho de código:

static public void assertEquals(Object expected, Object actual) {
	assertEquals(null, expected, actual);

}

static public void assertEquals(String message, Object expected, Object actual) {
	if (expected == null && actual == null)
		return;
	if (expected != null && isEquals(expected, actual))
		return;
	else if (expected instanceof String && actual instanceof String) {
		String cleanMessage= message == null ? "" : message;
		throw new ComparisonFailure(cleanMessage, (String)expected, (String)actual);
	}
	else
	        failNotEquals(message, expected, actual);
}

private static boolean isEquals(Object expected, Object actual) {
	if (expected instanceof Number && actual instanceof Number)
		return ((Number) expected).longValue() == ((Number) actual).longValue();
	return expected.equals(actual);
}

Veja que o metodo assertEquals delega para o método isEquals cujo faz um instanceOf verificando se os objetos sendo comparados são do tipo Number. Caso sejam, o metodo os transforma em long para depois compará-los.
Bingo. Com isso compara-se apenas a parte inteira do número, logo, aquele primeiro teste passou.

Meu próximo passo foi procurar na lista de bugs do JUnit se este já era um velho conhecido.
Para minha não surpresa lá estava o danado.

Esse problema foi encontrado na versão 4.3.1 do JUnit, que vem no eclipse europa, e já foi corrigido, veja o metodo isEqual da versão 4.5

private static boolean isEquals(Object expected, Object actual) {
	return expected.equals(actual);
}

Agora sim …

Por que eu ainda estava usando o eclipse europa?
Eu sei lá. Só sei que, foi bem mais divertido que postar a pergunta e ficar dando F5 no aguardo da resposta.

Advertisements