Didjit Didjit - 1 month ago 13
Java Question

JMockIt Mocked Method not Mocking

[10/7/06 4:00 Edited example code]
I'm trying to test some code (using JMockIt with TestNG) without touching the database, but it appears a mocked method is still actually called. Here's the simplified setup:

class DBRow {
public DBRow() { }
public DBRow(Object obj) {
initialize(obj);
}
public void insert() {
actuallyAddRowToDatabase();
}
}

class MyObject extends DBRow {
MyObject(Object obj) {
super(obj);
}
public void insert(Object obj) {
doSomething(obj);
insert();
}
}

class Factory {
static MyObject createObject(Object obj1, Object obj2) {
MyObject newObj = new MyObject(obj1);
newObj.insert(obj2);
return newObj;
}
}


I wanted to mock the insert operation to prevent an insertion in the actual database, so I tried something like this:

@Test
public void testCreation() {
new Expectations(MyObject.class) {
MyObject mock = new MyObject(null) {
@Mock
public void insert(Object obj) { }
};
{
new MyObject(anyString); result = mock;
}};

MyObject test = Factory.createObject("something", "something else");
}


But it appears that the real
insert(Object)
is still being called. I'm specifying that the class is mocked so all instances should be mocked, right? And I'm specifying that the insert method should be mocked, so why would the real method be getting called?

There's also a second problem with the above. When I define the mock class inside the Expectations block (as above), it seems that only the
Row()
constructor is being called instead of
Row(Object)
, and thus the object is not correctly initialized. I fixed this by moving it into a
@BeforeTest
method and instantiating the class there. Here's what that looks like:

private MyObject mock;

@BeforeTest
public void beforeTest() {
new MockUp<MyObject>() {
@Mock
public void insert(Object obj) { }
};
mock = new MyObject("something");
}

@Test
public void testCreation() {
new Expectations(MyObject.class) {{
new MyObject(anyString); result = mock;
}};

MyObject test = Factory.createObject("something", "something else");
}


So this seems to get the correct constructor to be called, but it still seems that
insert()
is being called as well. Any insights?

Answer

I was finally able to write my tests such that the right constructor got called and no actual database operation occurred. I think part of the problem I was bumping into was that one of the methods in the Expectations block was getting a different object than I expected for a parameter I wasn't interested in. And I think the other part of my problem was mixing up the roles of a MockUp class and Expectations recordings.

The code under test didn't change. Here's my simplified example again for convenience:

class DBRow {
    public DBRow() { }
    public DBRow(Object obj) {
        initialize(obj);
    }
    public void insert() {
        actuallyAddRowToDatabase();
    }
}

class MyObject extends DBRow {
    MyObject(Object obj) {
        super(obj);
    }
    public void insert(Object obj) {
        doSomething(obj);
        insert();
    }
}

class Factory {
    static MyObject createObject(Object obj1, Object obj2) {
        MyObject newObj = new MyObject(obj1);
        newObj.insert(obj2);
        return newObj;
    }
}

Here's essentially what I ultimately ended up with for test code:

Object something;
Object somethingElse;

@BeforeTest
public void beforeTest() {
    new MockUp<MyObject>() {
        @Mock
        public void insert() { }  // override the "actual" DB insert
    };                            // of Row.insert()
}

@Test
public void testCreation() {
    MyObject mock = new MyObject(something);  // object correctly init'd
                                              // by Row.initialize(Object)
    new Expectations(MyObject.class) {{
        new MyObject(something);     result = mock;
        mock.insert(somethingElse);
    }};

    MyObject test = Factory.createObject(something, somethingElse);
}

The key points were that A) I created a MockUp class to override the DB operations, and B) created a local instance of that partially mocked instance to record my Expectations.