Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Henrique Barcelos

JUnit e assertEquals para objects

Recommended Posts

Boa noite, pessoal.

 

Estou na reta final de um trabalho da faculdade, executando alguns testes e me deparei com um problema:

Como verificar se dois objetos são iguais (suas propriedades são iguais) de forma genérica, ou seja, sem ter que implementar o método equals em todas as classes?

O problema é que eu tenho uma hierarquia relativamente grande (horizontalmente) e não queria ter que ficar sobrescrevendo esse método.

 

Grato.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Creio que não há como fazer o que você quer. Mas se você estiver trabalhando com herança, então pode ser que funcione algo. Imagine que você tem C que herda de B e que herda de A. Dessa forma uma instância de C é B que por sua vez também é A. Assim sendo, se A sobrescreve o método equals B e C possuem o mesmo equals de A. Todavia eu não encorajo isso. Pense em fazer um equals em cadeia. Implemente equals em C que chama equals de B e assim por diante.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pois é, acabou que por conta do prazo apertado para entregar o trabalho eu fiz uma baita gambi adequação técnica:

abstract public class ObjectVO {
public boolean isEquals(ObjectVO vo) throws Exception {
	Class self = this.getClass();
	Class other = vo.getClass();
	Object params[] = {};

	if (!self.getName().equals(other.getName())) {
		return false;
	}

	Pattern p = Pattern.compile("^get");
	Matcher m = null;
	for (Method mtd : self.getDeclaredMethods()) {
		m = p.matcher(mtd.getName());
		if (m.find()) {
			Object srcIvk = mtd.invoke(this, params);
			Object dstIvk = mtd.invoke(vo, params);
			if (mtd.getReturnType().isPrimitive()) {
				if (srcIvk != dstIvk) {
					return false;
				}
			} else {
				try {
					mtd.getReturnType().asSubclass(ObjectVO.class);
					if (srcIvk != null
							&& dstIvk != null
							&& !((ObjectVO) srcIvk)
									.isEquals((ObjectVO) dstIvk)) {
						return false;
					}
				} catch (ClassCastException e) {
					if (srcIvk != null && dstIvk != null
							&& !srcIvk.equals(srcIvk)) {

						return false;
					}
				}
			}
		}
	}
	return true;
}
}

 

Basicamente: eu itero sobre todos os métodos do objeto utilizando a API de reflexão e procuro por getters. Guardo o retorno de sua invocação e os comparo.

Funciona bem pois são objetos "burros":

public class UsuarioVO extends ObjectVO {

private String email;

private String senha;

private String nome;

private double saldo;

public UsuarioVO() {
	super();
}

public UsuarioVO(String email, String senha) {
	super();
	this.email = email;
	this.senha = senha;
}

public UsuarioVO(String email, String senha, String nome) {
	super();
	this.email = email;
	this.senha = senha;
	this.nome = nome;
}


public UsuarioVO(String email, String senha, String nome, double saldo) {
	super();
	this.email = email;
	this.senha = senha;
	this.nome = nome;
	this.saldo = saldo;
}

public String getEmail() {
	return this.email;
}

public void setEmail(String email) {
	this.email = email;
}

public String getSenha() {
	return this.senha;
}

public void setSenha(String senha) {
	this.senha = senha;
}

public String getNome() {
	return this.nome;
}

public void setNome(String nome) {
	this.nome = nome;
}

public double getSaldo() {
	return this.saldo;
}

@Override
public String toString() {
	StringBuilder buffer = new StringBuilder();
	buffer.append("[");
	buffer.append("email = ");
	buffer.append(this.getEmail());
	buffer.append(", nome = ");
	buffer.append(this.getNome());
	buffer.append(", senha = ");
	buffer.append(this.getSenha());
	buffer.append(", saldo = ");
	buffer.append(this.getSaldo());
	buffer.append("]");
	return buffer.toString();
}
}

 

Precisava disso para fazer testes no projeto e o deadline estava muito próximo. Como não tenho muita experiência com Java, acabei demorando mais do que gostaria, mas ainda assim, muito menos tempo do que implementar um "isEquals" classe a classe.

 

Enfim, é o "monstro mais bonitinho" que eu já criei... Espero não precisar fazer uma coisa dessas de novo... ehueuheheeh

 

[]s

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bem, pra começar, acho que você fez uma pequena confusão. Para se comparar objetos, por padronização, você deve sobrescrever o método equals da classe Object e não criar um método isEquals. Dessa forma você poderia fazer um assertEquals(obj1, obj2) do JUnit.

 

Normalmente não se verifica se todos os atributos de um objeto são iguais. Verifica-se apenas os atributos que são chave. Por exemplo, campos que tem valores únicos. Assim como o CPF de um usuário, o login de um usuário e etc. Com isso você tem um ganho de performance muito grande e ainda assim não implementa coisa a toa.

 

Olhando rápido, sua implementação de igualdade não parece ser muito confiável. Você tem uma série de pontos onde se pode lançar uma exceção, quando você poderia simplesmente retornar false se os atributos fossem diferentes. Sem contar que dar manutenção naquele método não seria nada agradável.

 

É de suma importância sobrescrever hashCode quando se sobrescreve equals. Você tem um ganho de performance considerável com isso. APIs como Collections fazem grande uso de hashCode e equals.

 

Se você usa alguma IDE como Eclipse ou NetBeans você tem a facilidade de obter uma implementação de equals e hashCode em suas classes.

 

Pra finalizar, um bom conselho pra você, seria sobrescrever hashCode e equals sempre que criar uma entidade.

 

Essa seria uma implementação viável de hashCode e equals de uma entidade que tenha entre seus atributos nome e descricao.

 

@Override

public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.getNome() == null) ? 0 : this.getNome().hashCode());
result = prime * result + ((this.getDescricao() == null) ? 0 : super.getDescricao().hashCode());
return result;
}

@Override

public boolean equals(Object obj) {

if (this == obj)
	return true;
if (obj == null)
	return false;
if (getClass() != obj.getClass())
	return false;
       // if (!super.equals(obj)) return false; // Poderia ter isso caso houvesse herança.
Pessoa other = (Pessoa) obj;
if (this.getNome() == null) {
	if (other.getNome() != null)
		return false;
} else if (!this.getNome().equals(other.getNome()))
	return false;
if (this.getDescricao() == null) {
	if (other.getDescricao() != null)
		return false;
} else if (!this.getDescricao().equals(other.getDescricao()))
	return false;
return true;
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então Virgil, nesse caso eu REALMENTE precisava verificar o objeto todo pois estava fazendo testes na camada de persistência. Por exemplo, eu atualizava o objeto e em seguida tentava buscá-lo no banco. Todos os dados deveriam bater.

 

Quanto ao método equals, eu realmente tentei sobrescrevê-lo, mas não estava funcionando.

As chamadas recursivas ao se verificar objetos VO que possuíam outros objetos VO não eram executadas e não consegui descobrir porque.

 

É de suma importância sobrescrever hashCode quando se sobrescreve equals. Você tem um ganho de performance considerável com isso. APIs como Collections fazem grande uso de hashCode e equals.

 

Ah sim, acho que imagino o porquê.

 

Entretanto, quanto ao exemplo de código, como eu faria se o objeto possuísse tipos primitivos?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Entretanto, quanto ao exemplo de código, como eu faria se o objeto possuísse tipos primitivos?

 

Bastaria comparar com o operador de igualdade " == ".

Compartilhar este post


Link para o post
Compartilhar em outros sites
Me referia ao hash code...

Tipos primitivos não entram no cálculo do hash code?

Ah tá! Bastaria multiplicar pelo próprio valor do tipo primitivo.

public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((this.getNome() == null) ? 0 : this.getNome().hashCode());
  result = prime * result + ((this.getDescricao() == null) ? 0 : super.getDescricao().hashCode());
  result = prime * result + this.atributoPrimitivo; // Pode ser um char também.
       return result;
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.