Home | History | Annotate | Download | only in testing
      1 /*
      2  * Copyright (C) 2007 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.testing;
     18 
     19 import static com.google.common.base.Preconditions.checkNotNull;
     20 import static junit.framework.Assert.assertEquals;
     21 import static junit.framework.Assert.assertTrue;
     22 
     23 import com.google.common.annotations.Beta;
     24 import com.google.common.annotations.GwtCompatible;
     25 import com.google.common.base.Equivalence;
     26 import com.google.common.collect.ImmutableList;
     27 import com.google.common.collect.Iterables;
     28 import com.google.common.collect.Lists;
     29 
     30 import java.util.List;
     31 
     32 /**
     33  * Tester for equals() and hashCode() methods of a class.
     34  *
     35  * <p>To use, create a new EqualsTester and add equality groups where each group
     36  * contains objects that are supposed to be equal to each other, and objects of
     37  * different groups are expected to be unequal. For example:
     38  * <pre>
     39  * new EqualsTester()
     40  *     .addEqualityGroup("hello", "h" + "ello")
     41  *     .addEqualityGroup("world", "wor" + "ld")
     42  *     .addEqualityGroup(2, 1 + 1)
     43  *     .testEquals();
     44  * </pre>
     45  * <p>This tests:
     46  * <ul>
     47  * <li>comparing each object against itself returns true
     48  * <li>comparing each object against null returns false
     49  * <li>comparing each object against an instance of an incompatible class
     50  *     returns false
     51  * <li>comparing each pair of objects within the same equality group returns
     52  *     true
     53  * <li>comparing each pair of objects from different equality groups returns
     54  *     false
     55  * <li>the hash codes of any two equal objects are equal
     56  * </ul>
     57  *
     58  * <p>When a test fails, the error message labels the objects involved in
     59  * the failed comparison as follows:
     60  * <ul>
     61  *   <li>"{@code [group }<i>i</i>{@code , item }<i>j</i>{@code ]}" refers to the
     62  *       <i>j</i><sup>th</sup> item in the <i>i</i><sup>th</sup> equality group,
     63  *       where both equality groups and the items within equality groups are
     64  *       numbered starting from 1.  When either a constructor argument or an
     65  *       equal object is provided, that becomes group 1.
     66  * </ul>
     67  *
     68  * @author Jim McMaster
     69  * @author Jige Yu
     70  * @since 10.0
     71  */
     72 @Beta
     73 @GwtCompatible
     74 public final class EqualsTester {
     75   private static final int REPETITIONS = 3;
     76 
     77   private final List<List<Object>> equalityGroups = Lists.newArrayList();
     78   private final RelationshipTester.ItemReporter itemReporter;
     79 
     80   /**
     81    * Constructs an empty EqualsTester instance
     82    */
     83   public EqualsTester() {
     84     this(new RelationshipTester.ItemReporter());
     85   }
     86 
     87   EqualsTester(RelationshipTester.ItemReporter itemReporter) {
     88     this.itemReporter = checkNotNull(itemReporter);
     89   }
     90 
     91   /**
     92    * Adds {@code equalityGroup} with objects that are supposed to be equal to
     93    * each other and not equal to any other equality groups added to this tester.
     94    */
     95   public EqualsTester addEqualityGroup(Object... equalityGroup) {
     96     checkNotNull(equalityGroup);
     97     equalityGroups.add(ImmutableList.copyOf(equalityGroup));
     98     return this;
     99   }
    100 
    101   /**
    102    * Run tests on equals method, throwing a failure on an invalid test
    103    */
    104   public EqualsTester testEquals() {
    105     RelationshipTester<Object> delegate = new RelationshipTester<Object>(
    106         Equivalence.equals(), "Object#equals", "Object#hashCode", itemReporter);
    107     for (List<Object> group : equalityGroups) {
    108       delegate.addRelatedGroup(group);
    109     }
    110     for (int run = 0; run < REPETITIONS; run++) {
    111       testItems();
    112       delegate.test();
    113     }
    114     return this;
    115   }
    116 
    117   private void testItems() {
    118     for (Object item : Iterables.concat(equalityGroups)) {
    119       assertTrue(item + " must not be Object#equals to null", !item.equals(null));
    120       assertTrue(item + " must not be Object#equals to an arbitrary object of another class",
    121           !item.equals(NotAnInstance.EQUAL_TO_NOTHING));
    122       assertEquals(item + " must be Object#equals to itself", item, item);
    123       assertEquals("the Object#hashCode of " + item + " must be consistent",
    124           item.hashCode(), item.hashCode());
    125     }
    126   }
    127 
    128   /**
    129    * Class used to test whether equals() correctly handles an instance
    130    * of an incompatible class.  Since it is a private inner class, the
    131    * invoker can never pass in an instance to the tester
    132    */
    133   private enum NotAnInstance {
    134     EQUAL_TO_NOTHING;
    135   }
    136 }
    137