Untestable Code - introduction

This is the first part of series of posts dedicated to the problems of unit-testing of some "untestable" code.

Sometimes, (more often than I'm ready to admit), I stumble over a piece of code which makes me doubt completely in my testing skills and sometimes even in unit testing in general. I've spend a lot of time trying to deal with such a "untestable" code. In this series of posts I'm gonna show you what I've learned.

Please notice the source code attached at the end of this text.

I plan not only to show how to test this "untestable" code, but for every technique/tool I will also check:

  • how to use it with maven
  • if I'm still able to generate code-coverage report
  • if tests are easy to understand
  • what changes are required in tested code

and probably many more things.

One important thing here. It's not one of this hello-world-tutorials, where everythings works fine, there are no problems etc. NO ! The point of this series is to show problems (sometimes unsolved, yes) and shortcomings of different technologies/tools.

I assume that you are familiar with unit testing frameworks and with basic unit-testing techniques including mock objects.

I'm talking about unit testing here. No databases, no application context, no such things. Oh, come on, don't put all the tests into one bag, there are strong differences between unit/integration/system/user tests ! Stop calling any test you see "unit test" or people will keep laughing behind your back. ;)

What I'm gonna test ?

This class taken from JMockit tutorial:

public final class ServiceA {

    public void doBusinessOperationXyz(EntityX data)
        throws InvalidItemStatus {

        List items = Database.find(
            "select item from EntityY item where item.someProperty=?",
            data.getSomeProperty());
        BigDecimal total = new ServiceB().computeTotal(items);
        data.setTotal(total);
        Database.save(data);
    }

}

See attachment at the end of this text for sources of classes that will be tested.

There are few things worth testing in here:

  1. if total was computed by serviceB
  2. if data's total field is updated with the value computed by serviceB
  3. if data is saved by Database

What's so special about this code ?

Few things to notice about this code:

  • use of static methods (Database.find)
  • constructors (new ServiceB())
  • final classes (final class ServiceA)

Every aspect of this code mentioned here makes this code "untestable". Why ? Cause it calls directly classes which can't be easily mocked. Proxy-based mock libraries (like EasyMock or jMock) are useless here, because collaborators are not injected (like in DI) or passed to methods as arguments - they are static (like Database) or created on the spot (like ServiceB). There are no interfaces which are easier mockable than classes - only hard coded class names are used (in general mocking frameworks can deal not only with interfaces but also with classes, but it surely doesn't make our task easier). And as these classes are final you can't subclass, what is quite a common testing trick.

Yes, I know, this code is ugly. Yes, I know, we have IOC, we should code to interfaces rather than to classes, yes, I know all of this. But the point here is to start with such an ugly code and see what can be done about this.

Solutions which work

I plan to describe my experiments with various ways/ideas/tools used in order to test the code mentioned above.

The solutions can be divided into two sets:

1) no code changes needed

  • aspects - no, I won't waste my time describing this. Of course you can do almost everything with AspectJ, but the outcome will be probably similar (but inferior) to JEasyTest library, so why should I bother with reinventing the wheel ?
  • jeasytest library
  • jmockit library

2) code changes needed

Solutions which don't work (lol)

There are also technologies that I thought are able to deal with such a code, but I was wrong:

  • groovy - no, can't do it with Groovy !
  • jtestr - no, can't do it with JTestR !

Solutions which I won't describe in details (lol)

And here comes some more techniques, that I probably won't bother to explain in full details, but I'll give you links to sites, where they are well-explained - so you can see for yourself.

Attachments

See attached file (uc-base.zip) for code (maven project).

links

AttachmentSize
uc-base.zip10.21 KB

This is a really entertaining

This is a really entertaining Blog that you've put up there. I signed up to your Site's Rss and hope you write more articles that are equally great.
Cheers ;-)

Cobertura vs Clover

Tomek,

I came here from a link saying that it's a comparison between Clover and Cobertura code coverage tools. What happened to this article?

Regards,
Piotr

alas, some posts are gone :(

Hello Piotr,

unfortunately after technical disaster that hit my blog some time ago I had no time to post all old articles - "clover vs cobertura" among them. Sorry.

BTW. If I hadn't posted it, it means that I considered this article to be not so valuable... In other case I would have bring it back to life.

--
Tomek

Please comment using