11 October 2006

How do you mock casts?

An occasional question that comes up on the jMock users' mailing list is how to mock casts? This is, I guess, mostly needed when interfaces are used to define capabilities of an object that are dynamically discovered by some other object in the system, somewhat like this:
public class Juicer {
    public void juice(Juiceable fruit) {
        if (fruit instanceof Peelable) ((Peelable)fruit).peel();
        fruit.juice();
    }
}
If you create a mock Juiceable object when testing the Juicer, how can you create an expectation for the cast to Peelable and make it return a mock Peelable? Casts and the instanceof operator are implemented at the language/VM level and cannot be mocked out. However, you don't need to mock them at all . You can instead define an interface in your test that can be cast to the required interface and mock that.
public class JuicerTest extends MockObjectTestCase {
    interface PeelableAndJuiceable implements Peelable, Juiceable {}

    public void testPeelsPeelableFruitBeforeJuicing() {
        PeelableAndJuiceable fruit = mock(PeelableAndJuiceable.class, "fruit");
        Juicer juicer = new Juicer();

        expects(new InThisOrder() {{
            exactly(1).of (fruit).peel();
            exactly(1).of (fruit).juice();
        }});

        juicer.juice(fruit);
    }
}
David Saff makes a good point: "[You might] find that the new interface captures a new abstraction in your code that is useful in other places. In my experience, if it's worth testing, it's often worth naming."

2 comments:

Anonymous said...

i'd like a sample in C# for NMock2 please.

Thanks.

AC.

Travis said...

public interface IPeelableAndJuiceable : IPeelable, IJuiceable { }

private IPeelableAndJuiceable m_produce;

.
.
.

Expect.Once.On(Pantry).Method("GetProduce").
With(Is.EqualTo("Fruit")).
Will(Return.Value(m_produce));