5

I have two methods in the same helper class one that creates an image (createImage) and one that compares two images (compareImages).

Image createImage(Data data);
Boolean compareImages(Image from, Image to);

I want to check the return of createImage against an existing image. To do this I need a method exactly like compareImages.

Is it OK to use compareImages to validate createImage even if they both belong to the subject? This feels nasty to me.

RyanCosans
  • 209
  • 1
  • 7

3 Answers3

9

Despite established wisdom in writing unit tests, Martin Fowler has said that his idea of a unit is generally

"a bunch of closely related classes and treat them as a single unit. Rarely I might take a subset of methods in a class as a unit."

So there's no real reason why you shouldn't be unit testing a couple of tightly related methods as one unit. Obviously you might want to test method B independently passing in 2 images you know are the same, or are different to ensure it works, but once tested - use it as the test condition for your generation method.

There's nothing nasty in being practical, nasty is when ideology forces you to suffer.

gbjbaanb
  • 48,354
  • 6
  • 102
  • 172
5

It's important to understand that a single method may not be your "unit". To give a contrived example, if (for some reason) you created a class with public methods:

Add(object key, object value);
Get(object key);

which just wrapped a private Dictionary, it would be inconceivable to test just one of those methods at a time, independent of the other.


For the exact situation you describe, though, it may not be so clear-cut. It sounds like rather than being intrinsically dependent on each other for their function like the above example, your methods are linked only in that one happens to be useful in a test of the other.

In this case, it might be informative to start with the idea of duplicating method B exactly in your test code and asking whether or not this is a violation of DRY. Obviously you're repeating your code, but as demonstrated by Mathias Verraes, this doesn't always mean a DRY violation. A good way to think about this is: when one of these methods changes, will the other one always require the same change?

It's hard for me to judge without knowing the exact methods, but it's quite possible that you might want, for example, your test version of B not to do some validation that the production version does, or similar. In this case, having two versions may be fine.


There are additional practical trade-offs:

  • If you do use method B in testing method A, then a bug in method B could mask a bug in method A, or at least prevent you from knowing the true result of your test of method A. This is similar to the reason that people generally try to avoid multiple asserts.

  • On the other hand, if you make a test version of method B and allow it to vary independently of the production version, the test version itself will not be as well tested, which could be a problem if the method is sufficiently complex or liable to change.

Ben Aaronson
  • 6,883
  • 1
  • 30
  • 30
  • I've tweaked the question from A and B to actual method names. You might want to update your answer. –  Jan 26 '16 at 17:56
0

A unit test should be able to use any code or logic that itself is properly tested.

A unit test should have minimal logic in it. It should be straight forward as to its test and that one can look at it and reason about it.

Reimplementing an equals method violates DRY. In doing so, it introduces the possibility of having equals diverge and a bug slip in to the actual code being tested because it is still using an old copy of the code. It also adds non-trivial logic to the unit test.

So, going back to the first point - if equals is properly tested, there is no issue with using it in another unit test. In fact, one should encourage its use. The danger of false passes in this test would only be an issue if the tests for equals were to miss those conditions and failed to fail.

You do not expose yourself to any more bugs in your code by using more than a narrowly defined 'unit' worth of code in the test. You only expose yourself to not catching a bug in a more than ideally complex unit test if the code that it leverages is less than ideally tested itself.

Don't get caught up in the dogma of "a unit test must be..." If the test makes more sense written leveraging code that you've already tested, write the test that makes sense.