开发者

JMock Generic return types

I'm writing a JMock test for a class that needs to create a number of collections within itself. I am supplying the class with a factory which will generate a Collection when needed.

interface Factory
{
    <T> Collection<T> newCollection();
}

class MyClass
{
    public MyClass(Factory f)
    {
        List<ThingA> la = f.newCollection();
        List<ThingB> lb = f.newCollection();
    }
}

Now that works but when using JMock to test "MyClass", I cannot mock this return type overloading.

Collection<ThingA> ta = new LinkedList<ThingA>();
Collection<ThingB> tb = new LinkedList<ThingB>();
Collection<ThingC> tc = new LinkedList<ThingC>();

Factory mockFactory = context.mock(Factory.class);
context.checking(new Expectations()
{
    {
        allowing(mockFactory).newCollection(); will(returnValue(ta));
        allowing(mockFactory).newCollection(); will(returnValue(tb));
        allowing(mockFactory).newCollection(); will(returnValue(tc));
    }
}
);

// All return ta
Collection<ThingA> ta2 = mockFactory.newCollection();
Collection<ThingB> tb2 = mockFactory.newCollection();
Collection<ThingC> tc2 = mockFactory.newCollection();开发者_StackOverflow社区

Is there any way to get this to work? I know I could pass in a ThingX in as an argument but that seems a bit pointless if it's just to trigger type checking for testing.

My current fix is going to be to add a sequence so that I'm enforcing the order of the calls to newCollection but I can see situations where this would not work (say pooling of generic types).

Can this be done?


Type erasure is getting in the way to what you are trying to do. I'd simply pass a ThingX (or ThingX.class) as you mentioned. Unfortunately, type erasure forces you to do that type of hacky things.

It's best to think of your code as divided into two domains: generic-aware and generic-unaware, and when you have to glue things together from one the later to the former, there is no way to avoid doing one of the two (for keeping things under control):

either pass parameters just to trigger type checking (as you suggested):

Collection<ThingA> ta2 = mockFactory.newCollection(ThingA.class);
Collection<ThingB> tb2 = mockFactory.newCollection(ThingB.class);
Collection<ThingC> tc2 = mockFactory.newCollection(ThingC.class);

or encapsulate generics-unaware code into methods that are a) generics-aware, and b) have a @SuppressWarnings("unchecked") annotation to suppress the warning you'll get when assigning from one domain to the other.

class MockFactoryThingie
{
   /**
    *@SuppressWarnings("unchecked")
    */
   Collection<ThingA> newThingACollection()
   { 
     return (Collection<ThingA>) ... your generic-unaware collection thing...   
   }
}

Either way is clunky. We gotta thank our Java/JCP overlords for bestowing unto us this gem that is type erasure :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜