tags:

views:

785

answers:

5

We have a URL object in one of our Java classes that we want to mock, but it's a final class so we cannot. We do not want to go a level above, and mock the InputStream because that will still leave us with untested code (we have draconian test coverage standards).

I've tried jMockIt's reflective powers but we work on Macs and there are problems with the Java agent handler that I haven't been able to resolve.

So are there any solutions that do not involve using real URLs in the junit test?

+10  A: 

When I have a class that can't be easily mocked because it is final (or sealed in C#), my usual route is to write a wrapper around the class and use the wrapper wherever I would use the actual class. Then I would mock out the wrapper class as necessary.

tvanfosson
Yes but then I need to unit test the wrapper, with the URL class inside, and the problem persists.
bowsie
No. The wrapper is really thin and just reflects the methods on URL. You shouldn't need to unit test it, you should be able to verify it by inspection.
tvanfosson
Hmm, yes this is true. As I said above, it's that we have quite draconian coverage standards, and I would need to except the wrapper from our coverage. But this is the initial solution we came up with and I guess it makes sense. Thanks!
bowsie
"quite draconian coverage standards" - code coverage is a negative metric, meaning that it only tells useful info when you *dont* have coverage. once you got coverage, it doesnt really tell you anything about the quality of the code. I.e., having coverage does not indicate well tested code. i suggest you try to change the draconian standards if you can.
Chii
+4  A: 

I have used a URLHandler that allows me to load a URL from the classpath. So the following

new URL("resource:///foo").openStream()

would open a file named foo from within the class path. To do this, I use a common utility library and register a handler. To use this handler, you just need to call:

com.healthmarketscience.common.util.resource.Handler.init();

and the resource URL is now available.

Rob Di Marco
IMO, it's much better to use the URL constructor rather than mess with statics in the URL class.
Tom Hawtin - tackline
+2  A: 

I would look again at why you want to mock a final data object. Since by definition you aren't subclassing the object in your actual code, and it's not going to be the object under test, you shouldn't need to white-box test this code; just pass in whatever (real) URL objects are appropriate, and check the output.

Mock objects are useful when it's difficult to create a real object appropriate, or the real object's method are either time-consuming or depend on some stateful external resource (like a database). Neither of these apply in this case so I can't see why you can't just construct a real URL object representing the appropriate resource location.

Andrzej Doyle
Sure, it's the desire to decouple the tests from a real url of any kind, and when we call openConnection we open a new dependency.
bowsie
I think this is a legit problem. Mocking frameworks shouldn't hide from these kinds of things...
cwash
A: 

Does the URL class implement an interface? If so then you could instantiate it using inversion of control or a configurable factory, rather than by direct construction, this would allow you to inject/construct a test instance at test runtime rather than the final instance you currently have.

Joel
No as I said above URL is a final class, it's from the JDK.
bowsie
OK, i read the questions as "we have a URL object implemented by some final class (say MyURL, which for all I know could have an interface) rather than we have an instance of the java.net.URL class itself. I think the question could have been slightly more explicit.
Joel
Sure it could have been more explicit, it's a bit ambiguous as to whether it's a custom URL, apologies - but it still says it's final so...
bowsie
something can be final and still implement an interface, lending itself to factory or injection construction.
Joel
I'm +1'ing this back - it shouldn't be -1 because you *could* create a factory to do the exact same thing your wrapper class (in the accepted answer) does. Functionally equivalent, and what I would probably do if I had to take a first stab.
cwash
+1  A: 

JMockit does indeed allow you to mock a final JRE class like java.net.URL.

It seems the Attach API in jdkDir/lib/tools.jar available in implementations of JDK 1.6 other than Sun's does not work as well. I guess this stuff is still too new/advanced, or simply didn't get the necessary attention from the other JDK vendors (Apple, IBM with the J9 JDK, Oracle with the JRockit JDK).

So, if you run into problems by having tools.jar in the classpath, try using the "-javaagent:jmockit.jar" JVM argument. It tells the JVM to directly load the java agent at startup, without using the Attach API. That should work in the Apple JDK 1.5/1.6.

Rogerio
+1 for supporting your framework :)
cwash