Mai 132008
 

Are mock objects serializable? Sound’s like an idiot’s question, since no sane guy would ever consider serializing something that doesn’t really exist – that’s like trying to go to the basement of a movie mockup building.

However, as it often happens in a programmer’s life, sometimes you’re forced to behave weird. Especially with mock objects if you’re trying to feed them into a third-party library.

What I was doing is mocking parts of my application to do unit tests on a business process workflow written with Jboss’ jBPM. Specifically, I was testing a script embedded in the workflow’s XML (which prevents me from skipping the whole jBPM business and do straight tests on my Java classes).

The script calls an object that I have mocked, and I want to verify that the result gets written to the workflow’s execution context (that’s the data storage underlying it, basically a String->Object map):

There’s one big problem: jBPM is a framework with intrinsic persistence (using Hibernate) and any object written to a workflow’s execution context must be serializable (!). As I found out (the hard way, as it is often with these things): mock objects created using EasyMock (specifically, EasyMock Class Extensions) are *not* serializable, even if the class they pose as is…

When I ran straight into NotSerializableException, I tried to find ways around it. A possible tsrategy would be to add a special type converter to jBPM that allows mocks – but changing the code to be tested for testing is bad.

I thought of a lot bad solutions, I have to admit. However, the one I finally came up with is this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MySerializableTest {
 
	/**
	 * The serializable class to mock.
	 */
	public static class MySerializable implements Serializable {
		private static final long serialVersionUID = -242728243854443748L;
	}
 
	/**
	 * The class that will be actually mocked. Neither the
	 * {@link MockedMySerializable#readExternal(ObjectInput)} nor
	 * {@link MockedMySerializable#writeExternal(ObjectOutput)} method will do
	 * anything - they are just there to be mocked.
	 */
	public static class MockedMySerializable extends MySerializable implements Externalizable {
 
		@Override
		public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException 
                {
			/* do nothing */
		}
 
		@Override
		public void writeExternal(ObjectOutput out) throws IOException {
			/* do nothing */
		}
	}
}

This does the trick: instead of mocking the class that implements java.io.Serializable, I create a subclass of it that implements java.io.Externalizable (for those who don’t know: that’s kind of the ‘big brother’ of Serializable which lets the user implement the actual serialization himself. Of course, I provide only a dummy implementation. Nothing will be serialized for real here, it’s just to keep the code that invokes serialization from crying.

Of course, the mock created must either be a nice mock or actually expect the writeExternal(..) method to be called. But that’s just a routine task…