Unit testing - implementing equals only to facilitate testing - java

Here are my requirements for unit testing:
I would like to unit test my production classes
I would like to separate test code and production code apart such that I can release production code only
This seems like reasonable requirements. However, a problem always arises when I need to use methods such as assertEquals on objects as these requires that the equals method is overridden. The equals method would have to be implemented in production classes but is actually only used for testing. This becomes even worse when good coding practices dictates that if equals is overridden, then should hashCode also be implemented resulting in even more unused production code that clutters the production classes.
Here is a simple example with a User model (IntelliJ autoimplemented equals and hashCode)
public class User
{
public long id;
public long companyId;
public String name;
public String email;
public long version;
#Override
public boolean equals(Object o)
{
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if(companyId != user.companyId) return false;
if(id != user.id) return false;
if(version != user.version) return false;
if(!email.equals(user.email)) return false;
if(!name.equals(user.name)) return false;
return true;
}
#Override
public int hashCode()
{
int result = (int) (id ^ (id >>> 32));
result = 31 * result + (int) (companyId ^ (companyId >>> 32));
result = 31 * result + name.hashCode();
result = 31 * result + email.hashCode();
result = 31 * result + (int) (version ^ (version >>> 32));
return result;
}
}
As it can be seen, equals and hashCode takes up a lot of space and clutters the class.
One solution to the problem could be to create a class, UserTester, which could have an assertUserEquals method that could be used instead of eg. JUnit's assertEquals.
Another solution could be to create a UserComparator. However, it does not seem like JUnit have any assertEquals that takes a Comparator.
What is best practices on this point?

Uniutils has a perfect reflection equals method you can use for unit testing. This way your production code remains clear from all this test stuff.
public class User {
private long id;
private String first;
private String last;
public User(long id, String first, String last) {
this.id = id;
this.first = first;
this.last = last;
}
}
Later in test:
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);
If you're using Mockito it has it's own means to do the same thing:
Mockito.verify(userDeleter).delete(Mockito.refEq(user));

Not the most efficient but one possible way is to compare fields using reflection.
public class Foo {
int x;
Foo(int in){
x = in;
}
public static void main(String[] args) throws Exception{
Foo o1 = new Foo(1),o2= new Foo(1);
boolean allMatch = true;
Class<?> c = Class.forName("Foo");
Field[] fields = c.getDeclaredFields();
for(Field f: fields){
allMatch &= f.get(o1)==f.get(o2);
}
}
}

I see two different things:
Sometimes it's not desirable to override hashcode&equals. It can change behavior of your program and it can hurt your performance, see Java Overriding hashCode() method has any Performance issue?
If there is no customer requirement to override hashcode&equals, like it's value object, you would not do that. Your should provide such code which exactly meets customer criteria, not more. Your test should deal with original implementation in default object.

Related

Why does my equals method not recognize object variables?

I am simply trying to write an equals method that compares students names and sections. If the names and sections are the same, then the equals method should print true. Otherwise it should print false.
Below is what I have so far.
public class Student {
private String name;
private int section;
public Student(String name, int section) {
this.name = name;
this.section = section;
}
public boolean equals(Object y) {
if (this.name.equals(y.name) && this.section.equals(y.section)) {
return true;
}
else {
return false;
}
}
}
The error is with y.name and y.section. Eclipse tells me name and section cannot be resolved to a field.
My question is, can anybody show me how to fix my code so that I can compare student names and sections using the .equals() method?
#Override // you should add that annotation
public boolean equals(Object y) {
Your y is any Object, not necessarily a Student.
You need to have code like
if (y == this) return true;
if (y == null) return false;
if (y instanceof Student){
Student s = (Student) y;
// now you can access s.name and friends
Hmm.. I'm not sure, but I think Eclipse should this function too - 'add standard equals method' - use it and then your IDE generate absolutely right equals method... But it is about coding speed optimization. Now let's tell about equals method. Normally equals method contract defines transitiveness on itself... So if a equal to b then b equal to a. In this case it is recommended to have strict restrictions:
public boolean equals(Object x) {
if (x == this) {
return true; // here we just fast go-out on same object
}
if (x == null || ~x.getClass().equals(this.getClass())) {
return false; // in some cases here check `instanceof`
// but as I marked above - we should have
// much strict restriction
// in other ways we can fail on transitiveness
// with sub classes
}
Student student = (Student)y;
return Objects.equals(name, student.name)
&& Objects.equals(section, student.section);
//please note Objects - is new (java 8 API)
//in order of old API usage you should check fields equality manaully.
}
You are missing to type cast Object to Student class;
Student std = (Student)y;

Testing an overriden equals method

I'm reading J. Bloch's effective Java and needed to brush up the equals/hashCode contracts and relationships.
I have the following JavaBeans class:
public class MyJavaBean{
private int id;
private Properties fItem; //Enumeration type
private String value;
private Rule rule; //Enumeration type
private int fId;
//GET, SET
#Override
public boolean equals(Object o){
if(!(o instanceof MyJavaBean))
return false;
MyJavaBeanv = (MyJavaBean) o;
return (fItem == null ? v.fItem == null : fItem.equals(v.fItem)) &&
(value == null ? v.value == null : value.equals(v.value)) &&
(rule == null ? v.rule == null : rule.equals(v.rule)) &&
fId == v.fId && id == v.id;
}
#Override
public int hashCode(){
int result = 17;
if(fItem != null)
result = 31 * result + fItem.hashCode();
if(value != null)
result = 31 * result + value.hashCode();
if(rule != null)
result = 31 * result + rule.hashCode();
result = 31 * result + fId;
result = 31 * result + id;
return result;
}
}
He also suggest we write unitTests to make sure the contracts are actually satisfied. Here is what he said(italic-bold emphasize mine):
When you are finished writing your equals method, ask yourself three
questions: Is it symmetric? Is it transitive? Is it consistent? And
don’t just ask yourself; write unit tests to check that these
properties hold!
So, I can't imagine how to write unit test for that pretty straightforward case. Well, I'd write something like that:
public class MyTest{
//Each pair consists of the equals object
private Map<MyJavaBean, MyJavaBean> equalValueMap;
#Before
public void init(){
//initializing the map, using ThredLocalRandom
//to get ints and enums randomly
}
#Test
public void testReflexive(){
for(MyJavaBean fiv: equalValueMap.keySet()){
Assert.assertEquals(fiv.equals(fiv), true);
}
}
#Test
public void testSymmetric(){
for(MyJavaBean fiv: equalValueMap.keySet()){
Assert.assertEquals(equalValueMap.get(fiv).equals(fiv), true);
Assert.assertEquals(fiv.equals(equalValueMap.get(fiv)), true);
}
}
#Test
public void testHashCode(){
for(FilterItemValue fiv: equalFilterValueMap.keySet()){
Assert.assertEquals(equalFilterValueMap.get(fiv).hashCode(), fiv.hashCode());
}
}
}
But I think such tests just waste the build-time, because of their simplicity. Is it worthy to write tests for the methods for simple JavaBeans?
IMHO! You can safely skip the tests for equals() and hashCode() if they are generated by an IDE / some helping library you trust.
Although, if you're really doing some complex things and worry if these methods may not behave as desired in runtime, it might be worth spending time and writing a simple test.

getting an object from an arrayList with objects attribute

I have 2 classes.
public class klass1 {
String bir;
String myID;
klass1(String bir, String myID)
{
this.bir=bir;
this.myID=myID;
}
}
.
import java.util.*;
public class dd {
public static void main(String[] args) {
ArrayList<Object> ar=new ArrayList();
ar.add(new klass1("wer","32"));
ar.add(new klass1("das","23"));
ar.add(new klass1("vz","45"));
ar.add(new klass1("yte","12"));
ar.add(new klass1("rwwer","43"));
ar.remove(new klass1("vz","45"));//it's not worked!!!
System.out.println(ar.size());
}
}
What I want is removing or getting an object from array list with object's second attribute. How can I do that? Is there an easy way for it?
Just implement the equals method in the class Klass1.
public class Klass1 {
String bir;
String myID;
Klass1(String bir, String myID)
{
this.bir=bir;
this.myID=myID;
}
public boolean equals(Object o){
if(o instanceof Klass1)
return ((Klass1)o).myID.equals(myID);
else
return false;
}
}
Its because you are trying to delete a new object which isnt in the arraylist. When you use new klass1("vz","45") you are creating a new instance of this class which isnt in the arraylist.
What the system does internally is to compare those classes using equals. Why this doesn't work is explained in the following code:
Object o1 = new Object();
Object o2 = new Object();
System.out.println(o1 == o2); // false, obviously
System.out.println(o1.equals(o2)); // false
System.out.println(o1); // java.lang.Object#17046822
System.out.println(o2); // java.lang.Object#22509bfc
You can tell by the number following the # that these objects have a different hash values, and this is what the equals function of Object does check.
This is relevant for your klass, because unless you overwrite equals, you will use the equals of Object. And if you implement equals you should always implement hashcode as well. Because both tell you something about whether or not two objects are the "same", and if the one says something else than the other, some part of your code might get confused.
How to properly implement equals for your class:
#Override
public int hashCode() {
int hash = 7;
hash = 17 * hash + Objects.hashCode(this.bir);
hash = 17 * hash + Objects.hashCode(this.myID);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final klass1 other = (klass1) obj;
if (!Objects.equals(this.bir, other.bir)) {
return false;
}
if (!Objects.equals(this.myID, other.myID)) {
return false;
}
return true;
}
This can be done in most IDEs btw with a shortcut (i.E. alt-insert in Netbeans). Note that I did this in Java 7 using Objects. If you are in Java 6, you need to manually type(a == b) || (a != null && a.equals(b)); with the appropriate objects to compare.
Creating a proper hashcode is not always trivial, for more complex objects you might want to read a bit about hashcodes first. For simple objects: multiply primes with something.
The equals method is usually trivial, it is just important to first check for null and for class equality. This is often forgotten by programmers and a common source for NullPointerExceptions and ClassCastExceptions.

Compare two Java Collections using Comparator instead of equals()

Problem Statement
I have two Collections of the same type of object that I want to compare. In this case, I want to compare them based on an attribute that does not factor into equals() for the Objects. In my example, I'm using ranked collections of Names for instance:
public class Name {
private String name;
private int weightedRank;
//getters & setters
#Override
public boolean equals(Object obj) {
return this.name.equals(obj.name); //Naive implementation just to show
//equals is based on the name field.
}
}
I want to compare the two Collections to assert that, for position i in each Collection, the weightedRank of each Name at that position is the same value. I did some Googling but didn't find a suitable method in Commons Collections or any other API so I came up with the following:
public <T> boolean comparatorEquals(Collection<T> col1, Collection<T> col2,
Comparator<T> c)
{
if (col1 == null)
return col2 == null;
if (col2 == null)
return false;
if (col1.size() != col2.size())
return false;
Iterator<T> i1 = col1.iterator(), i2 = col2.iterator();
while(i1.hasNext() && i2.hasNext()) {
if (c.compare(i1.next(), i2.next()) != 0) {
return false;
}
}
return true;
}
Question
Is there another way to do this? Did I miss an obvious method from Commons Collections?
Related
I also spotted this question on SO which is similar though in that case I'm thinking overriding equals() makes a little more sense.
Edit
Something very similar to this will be going into a release of Apache Commons Collections in the near future (at the time of this writing). See https://issues.apache.org/jira/browse/COLLECTIONS-446.
You could use the Guava Equivalence class in order to decouple the notions of "comparing" and "equivalence". You would still have to write your comparing method (AFAIK Guava does not have it) that accepts an Equivalence subclass instead of the Comparator, but at least your code would be less confusing, and you could compare your collections based on any equivalence criteria.
Using a collection of equivance-wrapped objects (see the wrap method in Equivalence) would be similar to the Adapter-based solution proposed by sharakan, but the equivalence implementation would be decoupled from the adapter implementation, allowing you to easily use multiple Equivalence criteria.
You can use new isEqualCollection method added to CollectionUtils since version 4. This method uses external comparsion mechanism provided by Equator interface implementation. Please, check this javadocs: CollectionUtils.isEqualCollection(...) and Equator.
I'm not sure this way is actually better, but it is "another way"...
Take your original two collections, and create new ones containing an Adapter for each base object. The Adapter should have .equals() and .hashCode() implemented as being based on Name.calculateWeightedRank(). Then you can use normal Collection equality to compare the collections of Adapters.
* Edit *
Using Eclipse's standard hashCode/equals generation for the Adapter. Your code would just call adaptCollection on each of your base collections, then List.equals() the two results.
public class Adapter {
public List<Adapter> adaptCollection(List<Name> names) {
List<Adapter> adapters = new ArrayList<Adapter>(names.size());
for (Name name : names) {
adapters.add(new Adapter(name));
}
return adapters;
}
private final int name;
public Adapter(Name name) {
this.name = name.getWeightedResult();
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + name;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Adapter other = (Adapter) obj;
if (name != other.name)
return false;
return true;
}
}
EDIT: Removed old answer.
Another option that you have is creating an interface called Weighted that could look like this:
public interface Weighted {
int getWeightedRank();
}
Then have your Name class implement this interface. Then you could change your method to look like this:
public <T extends Weighted> boolean weightedEquals(Collection<T> col1, Collection<T> col2)
{
if (col1 == null)
return col2 == null;
if (col2 == null)
return false;
if (col1.size() != col2.size())
return false;
Iterator<T> i1 = col1.iterator(), i2 = col2.iterator();
while(i1.hasNext() && i2.hasNext()) {
if (i1.next().getWeightedRank() != i2.next().getWeightedRank()) {
return false;
}
}
return true;
}
Then as you find additional classes that need to be weighted and compared you can put them in your collection and they could be compared with each other as well. Just an idea.

Removing duplicates in TreeSet

I've been using ArrayList for my project to store a cricket team players and order them.
I started thinking about using a TreeSet because of its advantage of removing duplicates.
However the problem I'm having is that if for example I create the following two players:
P p1 = new P("Jack","Daniel",33(age),180(height),78(weight),41(games played),2300
(runs scored),41(dismisses))
P p2 = new P("Jack","Daniel",37(age),185(height),79(weight),45(games played),2560
(runs scored),45(dismisses))
Notice that the two players have the same first and last name, but everything else is different. When I try to add these two players to the TreeSet, it considers them duplicates because of the names similarities and removes the second one. Obviously I don't want this to happen and I want the Set to remove a player only if everything he has is the same as another player, and not just the first and last names.
Is there a way of achieving this?
Also my TreeSet takes a Player object.
Originally, this answer neglected the fact that a TreeSet does its comparisons based on compareTo(), rather than equals(). Edits have been made to address this.
You need to define equals(), hashCode() and compareTo() for your Player object correctly. (Since it's a TreeSet and not a HashSet, implementing hashCode() isn't so important - but it's good practice.)
Equals and hashCode need to take into account all of the fields. Eclipse can auto-generate one for you that will look similar to this (Source > Generate hashcode and equals).
If you already have a natural sort order that doesn't use all of the fields, then you could supply a custom comparator to your TreeSet. However, even if you really only want to sort by a subset of the fields, there's nothing stopping you sorting by all fields (with the uninteresting fields only playing a part of the interesting parts are identical). The important thing to note here is that a TreeSet determines equality not by the equals() method, but by compareTo() == 0.
Here's an example equals():
#Override
public boolean equals(Object obj)
{
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Player that = (Player) obj;
return this.age == that.age &&
this.height == that.height &&
this.weight == that.weight &&
this.games == that.games &&
this.runs == that.runs &&
this.dismisses == that.dismisses &&
this.given.equals(that.given) &&
this.family.equals(that.family);
}
And here's hashcode:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + this.age;
result = prime * result + this.dismisses;
result = prime * result + this.family.hashCode());
result = prime * result + this.games;
result = prime * result + this.given.hashCode());
result = prime * result + this.height;
result = prime * result + this.runs;
result = prime * result + this.weight;
return result;
}
Finally, here's a compareTo:
public int compareTo(Player that)
{
int result;
result = this.family.compareTo(that.family);
if (result != 0) // is the family name different?
{
return result; // yes ... use it to discriminate
}
result = this.given.compareTo(that.given);
if (result != 0) // is the given name different?
{
return result; // yes ... use it to discriminate
}
result = this.age - that.age; // is the age different?
if (result != 0)
{
return result; // yes ... use it to discriminate
}
... (and so on) ...
... with the final one ...
return this.dismisses - that.dismisses; // only thing left to discriminate by
}
a TreeSet instance performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the set, equal. The behavior of a set is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.
From Java Platform Standard Edition 8 Documentation TreeSet part.
class Student implements Comparable<Student> {
String name;
public Student(String name) {
this.name=name;
}
public String toString(){
return name;
}
public int compareTo(Student gStudent) {
if(!this.name.equals(gStudent.getName()))
return 1;
return 0;
}
private String getName() {
return name;
}
}

Categories