10

Is there any tool/library which can automatically generate the tests for my hashcode and equals methods looking at the instance variables involved in these methods?

Mangoose
  • 922
  • 1
  • 9
  • 17
  • Such tests have no substantial meaning, but mostly introduce maintenance. – M Platvoet May 17 '12 at 09:36
  • 4
    @MPlatvoet I don't really agree with you, testing `equals()` and `hashcode()` could be important especially if some of your code relies heavily on that (`HashMap`, equality test involving inheritance) – Colin Hebert May 17 '12 at 09:41
  • @Colin Hebert So then you would write a test upfront which lays down some requirements. Generating the test would only confirm the same possible mistakes you've introduced writing the implementation. So now it only becomes a maintenance addition when you have the need to change the implementation. – M Platvoet May 17 '12 at 09:47
  • @buymypies I am using eclipse – Mangoose May 17 '12 at 10:30
  • @MPlatvoet I agree with your thoughts that these tests will replicate the mistakes made in the source. But using this way we can ensure that no one accidentally drops a variable from these methods. – Mangoose May 17 '12 at 10:32
  • @manish Programmers are not in the business of accidentally changing code. They change code for a reason. A generated test method doesn't tell you the reason, the rational behind a method. So when a programmer feels the need to change the implementation they simply change the test-case accordingly. Having a test-case is not necessarily a good thing. (yes I know, not a popular statement, but simply a rational statement) – M Platvoet May 17 '12 at 10:46
  • This question has a sibling: http://stackoverflow.com/questions/9624043/technique-to-automatically-check-consistency-of-equals-hashcode-and-compareto - with almost the same but also some other answers. – Ralph Nov 07 '14 at 08:14

5 Answers5

7

Guava uses this test builder to test equals and hashCode.

tvStatic
  • 921
  • 1
  • 9
  • 26
Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
3

toString() should not have any "contract" to respect, so unit testing it would be weird and not useful.

You can take a look at this project regarding equals().

There is also a JUnit Addon EqualsHashCodeTestCase


On the same topic:

Community
  • 1
  • 1
Colin Hebert
  • 91,525
  • 15
  • 160
  • 151
  • 1
    I don't see why toString couldn't have a spec, although I've never unit-tested it myself. – Dave Newton May 17 '12 at 09:34
  • Well, I didn't say that it couldn't but it shouldn't. `toString()` is the String representation of an object so it is readable by a person (see javadoc for Object), so this says that basically it shouldn't do anything "extreme" (it's the representation of the object, not a business method) and that it returns a String made to be read by a human being, not to be parsed. From there it's easy to say that `toString` shouldn't be unit tested (not "can't" or "shan't"). – Colin Hebert May 17 '12 at 09:38
  • Yes, if you are blanket-testing all classes then it doesn't make sense. Do note that it is still legal for a specific class do make a stricter requirement on `toString`. There are quite a number of examples in the JSE itself (`StringWriter`, `StringBuilder`, `URL`, ...). – Marko Topolnik May 17 '12 at 09:41
  • I tend to agree (and have argued as much in other SO posts) but it's often used in web dev as a shortcut to display info on a web page, say. For simple DTOs I'm not as adamant about such abuse, I guess... But ultimately, I don't have an issue with enforcing what it means to be "human readable" in the context in the app in question. It's a relatively minor point; I just found it interesting to think about, 'cuz I never have :) – Dave Newton May 17 '12 at 09:43
  • @MarkoTopolnik I agree with you, sometimes `toString()` need to make a certain sense, but even in these cases, I'm not sure that a unit test is relevant. It's not the same thing as testing getters/setters, but it's somewhat close to that. The test itself will have next to no sense at all (and you'll end up checking more if concatenation works in java than testing the actual method). – Colin Hebert May 17 '12 at 09:46
  • @DaveNewton If your `toString()` method just prints a String (as it should do), there is nothing to actually test anyway. – Colin Hebert May 17 '12 at 09:47
  • Well, toString doesn't print a string, but I disagree--if it's supposed to contain specific instance information, and/or be in a specific format, how is that not a testable requirement? – Dave Newton May 17 '12 at 09:49
  • Sorry for the "prints", slip of the tongue. Regarding your testable requirement, I don't think that unit testing should be used to test formated content. If you have a method only used to format content it will be a simple concatenation of multiple values provided either by other methods of your class or by fields from your object. So if there is a test to be done, it should be on these methods(fields?), not on a method that concatenate values. What would the test be about? Checking that the concatenation is done in a certain order? That's useless (not worth testing). – Colin Hebert May 17 '12 at 09:57
  • Furthermore, I would say that anyone relying on `toString()` to execute actual code which wouldn't be only calls to accessors and fields (or methods without a side effect) relies on a potentially dangerous code, but that's only my opinion. – Colin Hebert May 17 '12 at 09:58
  • It's absolutely not useless, and testing an ordered (or formatted, or otherwise massaged) combination of data is not the same as testing individual data. Trivial example: this app will display phone numbers in the format (nnn) nnn-nnnn. It's stored in the object as three numbers. For whatever reason, toString is expected to do this. It's absolutely reasonable to have that be an executable spec. There's nothing "dangerous" about this--and if there was, yet another reason to test. Unit tests can be part of a spec, not just simple tests of mechanical correctness. – Dave Newton May 17 '12 at 10:02
  • What is tested? If a one liner with three `+` is written correctly? If `+` is behaving correctly? This for me makes as much sense as testing an accessor (not quite the same but it has the same implications). I wouldn't get angry at someone who would do that in their code, I would just say that's a loss of time. Once your entire code is covered and your code is finished, if you don't know what to do, go ahead and test `toString()` (it may be useful at some point), but otherwise, it's pointless. (PS: The "dangerous" part was about a toString method doing something else than concatenation, etc.) – Colin Hebert May 17 '12 at 10:16
3

EqualsVerifier is a great library. I'm often combining it with Reflections library to automatically scan for certain classes and test the contract for all of them at once:

 @Test
  public void validateEqualsHashCodeToString() {
    final Reflections dtoClassesReflections = new Reflections(new ConfigurationBuilder()
      .setUrls(ClasspathHelper.forPackage("my.base.package"))
      .filterInputsBy(new FilterBuilder()
        .include(".*Dto.*") // include all Dto classes
        .exclude(".*Test.*")) // exclude classes from tests which will be scanned as well
      .setScanners(new SubTypesScanner(false)));

    final Set<Class<?>> allDtoClasses = dtoClassesReflections.getSubTypesOf(Object.class);

    allDtoClasses.forEach(dtoClass -> {
      logger.info("equals/hashCode tester testing: " + dtoClass);
      EqualsVerifier.forClass(dtoClass).verify();

      try {
        dtoClass.getDeclaredMethod("toString");
      } catch (NoSuchMethodException e) {
        fail(dtoClass + " does not override toString() method");
      }
    });
  }
walkeros
  • 4,736
  • 4
  • 35
  • 47
1

I would recommend EqualsVerifier for testing hash code and equals method.

I would recommend ToStringVerifier for testing toString method

Here is an example:

@Test
public void testToString()
{
    ToStringVerifier.forClass(User.class).verify();
}
-1

You can use the Apache EqualsBuilder and HashCodeBuilder to implement equals and hashCode and thus minimize the risk of not doing it properly.

Testing equals is simple, create two instances with instance values the same (by which you will expect them to be equal) and invoke equals on an instance passing the other as a parameter, and you should expect it to return true :D

  • 1
    The question is, how can you test that the hashCode contract is not broken. How should one implement tests, that check following two things: 1. If equals returns true, then hashCode must return same values. 2. If hashCode return different values, then equals must return false. – mentallurg May 29 '14 at 18:25