7

Like the title says, is it a bad practice to create/generate the Object.equals() method because I need it in my unit tests, but not in my regular code?

Jelle
  • 2,014
  • 2
  • 12
  • 19
  • @KilianFoth in that question they're talking about modifying code, while I'm talking about actually adding pretty much 20~40 lines of code per domain object purely for JUnit. – Jelle Dec 05 '16 at 12:09
  • Years later and I am using Kotlin now which generates these equals method in the background, which is a great help :-) – Jelle Jun 06 '23 at 09:56

2 Answers2

5

Writing code to make testing easier is a good thing. Writing code just for the tests to work around badly written code is bad.

If you are considering writing an equals() method because you need it in your unit tests, then this should set alarm bells going. Why do you need it? Tests should exercise the public APIs of your code. If those APIs do not expose classes with custom equals() then you shouldn't need to test those classes in that way.

So yes, it is bad practice to add functionality to the core code just for use by unit tests. You are just papering over the cracks when you do so. Instead fix the real problem, to remove the need for such extra code.

Update As the purpose of this code is not to access encapsulated values, but to test the equality of to objects bases on publicly accessible values, then equals() is good: just not on the domain objects themselves. Create the method in the test class/package (depending on scope needed) and have your tests call that.

David Arno
  • 38,972
  • 9
  • 88
  • 121
  • This question was asked for a database test. What I wanted to achieve was something like: `assertEquals(myProject, databaseProject);`. Is it a good idea instead to assert each field seperately, like: `assertEquals(myProject.getId(), databaseProject.getId());`, which makes my test bigger, but does not affect the actual code? – Jelle Dec 05 '16 at 12:24
  • @Jelle, would it, by any chance, make your test "20~40 lines" bigger? ;) – David Arno Dec 05 '16 at 12:37
  • probably even more, since I have to do this for every time I want to assert... – Jelle Dec 05 '16 at 12:38
  • serialise the object to a file and compare with a known good file – Ewan Dec 05 '16 at 12:46
  • @Jelle, you could have a method within your test class that had the repeated assertions. – tylerwal Dec 05 '16 at 13:05
  • 2
    Sorry, overlooked your update, will remove my comment and downvote. Nevertheless I think adding an "equals" method to a domain object to make it more testable is nothing bad (as long as it is implemented correctly, and not sloppy because it is just "for testing"). – Doc Brown Dec 05 '16 at 13:53
  • Well, at least I agree not 100%. IMHO when someone implements "equals", it must be correct in terms of the domain - if that means not only publicly accessible values will have to be compared, then this is the correct way to go. Maybe only a test uses that `equals` method - initially - but once it is there, it is not unlikely other production code will start to reuse it later. – Doc Brown Dec 05 '16 at 14:07
  • @DocBrown A bit of a digression, but the specific problem with `equals` in languages such as Java is that it's very difficult to get right, and getting it wrong (like forgetting to also change `hashCode`, depending on mutable properties, or not making it reflexive) often has unintended consequences. So I favor not touching it unless you fully understand the consequences, and in this context, "making an object testable" will probably not lead to such an understanding :) Josh Bloch addresses the surprisingly many pitfalls in *Effective Java*. – Andres F. Dec 05 '16 at 17:39
  • @AndresF.: I see your point. Maybe we can agree upon "one can implement `equals` for testing purposes when the person knows what he/she is doing" - which is always a good prerequisite when writing code ;-) – Doc Brown Dec 05 '16 at 22:04
  • One solution for avoiding code duplication is creating a custom assertion - an object which wraps your tested object and provides a method to compare it for equality to another. This way the comparison logic is in one place while at the same time this place is in test code rather than production code. – Michał Kosmulski Dec 06 '16 at 08:28
0

Equality of objects in your unit tests is probably needed to verify results produced by production code. As public API of these results is all you want to check in tests , there is a simple, yet convenient way to perform same comparisons non-intrusively.

Create a descriptor class, that takes object in question as argument, inspects it's public API for interesting properties and makes them it's fields. Such descriptor is trivially constructible from field values and therefore could be used in tests easily:

class Descriptor {
    int field1;
    string field2;
    Descriptor(f1,f2){
        field1=f1;
        field2=f2;
    }
    Descriptor(IResult r) {
       this (r.getNumber (), r.getString ());
    }
    booolean equals(Descriptor d) {
       return field1.equals (d. field1) && field2.equals (d. field2);
    }

    string toString () {
       some test frameworks will be more helpful, provided with string representation of data under verification
    }
}


.....


 void test () {
     Descriptor  actual = new Descriptor (subject.produce ());
     Descriptor expected = new Descriptor (0, "Hello world!");
     assertEquals (expected, actual, "Should greet the world");
 }
Basilevs
  • 1,221
  • 9
  • 11