views:

918

answers:

6

Is there a way to mock object construction using JMock in Java?

For example, if I have a method as such:

public Object createObject(String objectType) {
    if(objectType.equals("Integer") {
        return new Integer();
    } else if (objectType.equals("String") {
        return new String();
    }
}

...is there a way to mock out the expectation of the object construction in a test method?

I'd like to be able to place expectations that certain constructors are being called, rather than having an extra bit of code to check the type (as it won't always be as convoluted and simple as my example).

So instead of:

    assertTrue(a.createObject() instanceof Integer);

I could have an expectation of the certain constructor being called. Just to make it a bit cleaner, and express what is actually being tested in a more readable way.

Please excuse the simple example, the actual problem I'm working on is a bit more complicated, but having the expectation would simplify it.


For a bit more background:

I have a simple factory method, which creates wrapper objects. The objects being wrapped can require parameters which are difficult to obtain in a test class (it's pre-existing code), so it is difficult to construct them.

Perhaps closer to what I'm actually looking for is: is there a way to mock an entire class (using CGLib) in one fell swoop, without specifying every method to stub out?

So the mock is being wrapped in a constructor, so obviously methods can be called on it, is JMock capable of dynamically mocking out each method?

My guess is no, as that would be pretty complicated. But knowing I'm barking up the wrong tree is valuable too :-)

A: 

I hope there is none. Mocks are supposed to mock interfaces, which have no constructors... just methods.

Something seems to be amiss in your approach to testing here. Any reason why you need to test that explicit constructors are being called ?
Asserting the type of returned object seems okay for testing factory implementations. Treat createObject as a blackbox.. examine what it returns but dont micromanage how it does it. No one likes that :)

Update on the Update: Ouch! Desperate measures for desperate times eh? I'd be surprised if JMock allows that... as I said it works on interfaces.. not concrete types. So

  • Either try and expend some effort on getting those pesky input objects 'instantiable' under the test harness. Go Bottom up in your approach.
  • If that is infeasible, manually test it out with breakpoints (I know it sucks). Then stick a "Touch it at your own risk" comment in a visible zone in the source file and move ahead. Fight another day.
Gishu
It is a simple factory method. The problem is that the factory creates wrappers for other objects (taking those in as a parameter during construction) and returns those. I'd like to be able to test that the constructor is being called, rather than mock out or instantiate the object being wrapped.
Grundlefleck
The problem with that is that the factory method takes an object which is hard to recreate in isolation - refactoring to allow that is pretty much out of the question for now. Although... mocking those is probably the answer...
Grundlefleck
+4  A: 

The only thing I can think of is to have the create method on at factory object, which you would than mock.

But in terms of mocking a constructor call, no. Mock objects presuppose the existence of the object, whereas a constructor presuppose that the object doesn't exist. At least in java where allocation and initialization happen together.

sblundy
A: 

Are you familiar with Dependency Injection?

If no, then you ceartanly would benefit from learning about that concept. I guess the good-old Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler will serve as a good introduction.

With Dependency Injection (DI), you would have a DI container object, that is able to create all kinds of classes for you. Then your object would make use of the DI container to instanciate classes and you would mock the DI container to test that the class creates instances of expected classes.

Rene Saarsoo
A: 

Dependency Injection or Inversion of Control.

Alternatively, use the Abstract Factory design pattern for all the objects that you create. When you are in Unit Test mode, inject an Testing Factory which will tell you what are you creating, then include the assertion code in the Testing Factory to check the results (inversion of control).

To leave your code as clean as possible create an internal protected interface, implement the interface (your factory) with the production code as an internal class. Add a static variable type of your interface initialized to your default factory. Add static setter for the factory and you are done.

In your test code (must be in the same package, otherwise the internal interface must be public), create an anonymous or internal class with the assertion code and the test code. Then in your test, initialize the target class, assign (inject) the test factory, and run the methods of your target class.

Javaxpert
A: 

Alas, I think I'm guilty of asking the wrong question.

The simple factory I was trying to test looked something like:

public Wrapper wrapObject(Object toWrap) {
    if(toWrap instanceof ClassA) {
        return new Wrapper((ClassA) toWrap);
    } else if (toWrap instanceof ClassB) {
        return new Wrapper((ClassB) toWrap);
    } // etc

    else {
        return null;
    }
}

I was asking the question how to find if "new ClassAWrapper( )" was called because the object toWrap was hard to obtain in an isolated test. And the wrapper (if it can even be called that) is kind of weird as it uses the same class to wrap different objects, just uses different constructors[1]. I suspect that if I had asked the question a bit better, I would have quickly received the answer:

"You should mock Object toWrap to match the instances you're testing for in different test methods, and inspect the resulting Wrapper object to find the correct type is returned... and hope you're lucky enough that you don't have to mock out the world to create the different instances ;-)"

I now have an okay solution to the immediate problem, thanks!

[1] opening up the question of whether this should be refactored is well out of the scope of my current problem :-)

Grundlefleck
+2  A: 

jmockit can do this.

See my answer in http://stackoverflow.com/questions/22697#93675

Kris Pruden
Yes! JMockit RULES! http://stackoverflow.com/questions/190597/how-to-go-about-mocking-a-class-with-final-methods
Epaga