View Javadoc
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  import java.util.List;
30  
31  /**
32   * Tester for equals() and hashCode() methods of a class.
33   *
34   * <p>The simplest use case is:
35   *
36   * <pre>
37   * new EqualsTester().addEqualityGroup(foo).testEquals();
38   * </pre>
39   *
40   * <p>This tests {@code foo.equals(foo)}, {@code foo.equals(null)}, and a few
41   * other operations.
42   *
43   * <p>For more extensive testing, add multiple equality groups. Each group
44   * should contain objects that are equal to each other but unequal to the
45   * objects in any other group. For example:
46   * <pre>
47   * new EqualsTester()
48   *     .addEqualityGroup(new User("page"), new User("page"))
49   *     .addEqualityGroup(new User("sergey"))
50   *     .testEquals();
51   * </pre>
52   * <p>This tests:
53   * <ul>
54   * <li>comparing each object against itself returns true
55   * <li>comparing each object against null returns false
56   * <li>comparing each object against an instance of an incompatible class
57   *     returns false
58   * <li>comparing each pair of objects within the same equality group returns
59   *     true
60   * <li>comparing each pair of objects from different equality groups returns
61   *     false
62   * <li>the hash codes of any two equal objects are equal
63   * </ul>
64   *
65   * <p>When a test fails, the error message labels the objects involved in
66   * the failed comparison as follows:
67   * <ul>
68   *   <li>"{@code [group }<i>i</i>{@code , item }<i>j</i>{@code ]}" refers to the
69   *       <i>j</i><sup>th</sup> item in the <i>i</i><sup>th</sup> equality group,
70   *       where both equality groups and the items within equality groups are
71   *       numbered starting from 1.  When either a constructor argument or an
72   *       equal object is provided, that becomes group 1.
73   * </ul>
74   *
75   * @author Jim McMaster
76   * @author Jige Yu
77   * @since 10.0
78   */
79  @Beta
80  @GwtCompatible
81  public final class EqualsTester {
82    private static final int REPETITIONS = 3;
83  
84    private final List<List<Object>> equalityGroups = Lists.newArrayList();
85    private final RelationshipTester.ItemReporter itemReporter;
86  
87    /**
88     * Constructs an empty EqualsTester instance
89     */
90    public EqualsTester() {
91      this(new RelationshipTester.ItemReporter());
92    }
93  
94    EqualsTester(RelationshipTester.ItemReporter itemReporter) {
95      this.itemReporter = checkNotNull(itemReporter);
96    }
97  
98    /**
99     * Adds {@code equalityGroup} with objects that are supposed to be equal to
100    * each other and not equal to any other equality groups added to this tester.
101    */
102   public EqualsTester addEqualityGroup(Object... equalityGroup) {
103     checkNotNull(equalityGroup);
104     equalityGroups.add(ImmutableList.copyOf(equalityGroup));
105     return this;
106   }
107 
108   /**
109    * Run tests on equals method, throwing a failure on an invalid test
110    */
111   public EqualsTester testEquals() {
112     RelationshipTester<Object> delegate =
113         new RelationshipTester<>(
114             Equivalence.equals(), "Object#equals", "Object#hashCode", itemReporter);
115     for (List<Object> group : equalityGroups) {
116       delegate.addRelatedGroup(group);
117     }
118     for (int run = 0; run < REPETITIONS; run++) {
119       testItems();
120       delegate.test();
121     }
122     return this;
123   }
124 
125   private void testItems() {
126     for (Object item : Iterables.concat(equalityGroups)) {
127       assertTrue(item + " must not be Object#equals to null", !item.equals(null));
128       assertTrue(item + " must not be Object#equals to an arbitrary object of another class",
129           !item.equals(NotAnInstance.EQUAL_TO_NOTHING));
130       assertEquals(item + " must be Object#equals to itself", item, item);
131       assertEquals("the Object#hashCode of " + item + " must be consistent",
132           item.hashCode(), item.hashCode());
133     }
134   }
135 
136   /**
137    * Class used to test whether equals() correctly handles an instance
138    * of an incompatible class.  Since it is a private inner class, the
139    * invoker can never pass in an instance to the tester
140    */
141   private enum NotAnInstance {
142     EQUAL_TO_NOTHING;
143   }
144 }