Wednesday, May 27, 2015

Who's Mock is it Anyways?

How to mock a third-party module for unit tests is a coding problem I often see pop up in design pattern boot-camps. In one boot-camp I teach the design involves using a third-party scale to weigh parcels.

This particular third-party scale returns a random weight each time it is used, so it cannot be used to test the validity of any calculations that depend on weight. So here is the thing: are we testing the adapter, or are we testing the client that uses the adapter? Because what we do in each of those situations is different.

If we are testing the client then the third-party scale is irrelevant. All that we need is a guaranteed value in response to calling the scale for the test. The easy solution demonstrated in figure 1 is to simply mock the adapter interface and inject the mock into the client where it needs the adapter. In fact, because the adapter is an interface it is a trivial mock to build using a framework like EasyMock.

But what about testing the adapter class that drives the third-party scale? Well, unfortunately that is a tortured path you should best leave alone. What? But we need to test the adapter!

Well, here's the rub: you can only mock interfaces, and sometimes a class. Most mock frameworks mock a class by extending it to build a new anonymous class and from that an object. But even going to those lengths cannot work if the class we want to mock is final. And there is a really good chance it will be, I rarely want to see you extending my code into your application.

So there is another possibility: replace the third-party code with your own code matching it package-name for package-name and class-name for class-name. But then the names conflict and your mock cannot be in the project at the same time as the third-party code. To make it work you need to replace the third-party library during the tests and swap it out after, and that defeats the goal of automated testing.

And that leaves only one other avenue: never test client code using the adapter, and only test the code in the adapter up to the point it interfaces with the third-party code. To perform such a test the third-party code needs to be disabled, and you need a way for the unit test to peek at how the adapter has done its work to set up the call. Building a unit test that can get inside of a class is usually undesirable, so we will leave that problem for another day.

The bottom line: test your client code vigorously. Mock the adapter interface and return the expected results. But pass on automated testing of the real adapter. Swapping out the third-party code is just too difficult, and frequent testing against the third-party code has other drawbacks like running a credit-card transaction over and over again.

Footnote: Mock Turtles only exist on the other side of the looking glass. During the 18th century green turtle soup was very popular in Great Britain, but also expensive. So mock turtle soup was an imitation, made out of nasty stuff that I do not want to mention here. You can always look it up!

No comments:

Post a Comment