如何在JMockit中使用通用对象模拟方法?

时间:2009-02-02 18:35:16

标签: java generics mocking jmockit

如果您知道如何使用JMockit,这个问题是自解释的:如何模拟一个具有泛型的方法?我想模仿这个方法:public T save(T entity)但它总是抛出这样的异常:

mockit.RealMethodNotFoundForMockException: Corresponding real methods not found for the following mocks:
Object save(Object)
    at mockit.internal.RedefinitionEngine.modifyRealClass(RedefinitionEngine.java:130)
    at mockit.internal.RedefinitionEngine.modifyRealClass(RedefinitionEngine.java:88)
    at mockit.internal.RedefinitionEngine.redefineMethods(RedefinitionEngine.java:72)
    at mockit.Mockit.setUpMocks(Mockit.java:197)
    at com.mytest.MyTest.setUp(AdminManagerImplTest.java:83)

4 个答案:

答案 0 :(得分:2)

为了完整起见:

我在Java 1.6模式下的Java 1.7,TestNG和jMockit 1.5和1.16中遇到了类似的问题,使用方法内的MockUp< ...>一堂课。

我的课程:

public class MyCache extends AbstractLazyCache<ExchangeObject> {...}

模拟方法:

 public abstract class AbstractLazyCache<CachedObject> extends AbstractCache {
      // ...

      protected void putCachedObject(String tenantId, String objectId, CachedObject o, Date expiryDate) {...}
  }

原始模仿尝试:

    new MockUp<MyCache>() {
        // invoked once manually, once on expired cache
        @Mock(invocations = 2)
        public void putCachedObject(Invocation inv, String tenantId, String objectId,
                RangeExchange o, Date expiryDate) {
            // expire one second in the future
            inv.proceed(tenantId, objectId, o, new Date(System.currentTimeMillis() + waitTimeMillis));
        }
    };

我得到了类似的例外。通过在公共之后添加附加通用参数,我的测试用例终于成功了:

    new MockUp<MyCache>() {
        // invoked once manually, once on expired cache
        @Mock(invocations = 2)
        public <RangeExchange> void putCachedObject(Invocation inv, String tenantId, String objectId,
                RangeExchange o, Date expiryDate) {
            // expire one second in the future
            inv.proceed(tenantId, objectId, o, new Date(System.currentTimeMillis() + waitTimeMillis));
        }

    };

答案 1 :(得分:1)

它对我有用,例如

package a;

import junit.framework.TestCase;
import mockit.Mockit;

public class JMockitCoreExampleTest extends TestCase {

    public class MockMe {
        public T save(T t) {
            return null;
        }
    }

    public void testDoOperationAbc() {
        Mockit.redefineMethods(MockMe.class, SayThatYouMockMe.class);
                MockMe mock = new MockMe();
        System.out.println(mock.save("aaa"));
    }

    public static class SayThatYouMockMe {
        public T save(T t) {
            return t;
        }
    }
}

输出

Loaded external tool: mockit.integration.junit3.JUnitTestCaseDecorator
Loaded external tool: mockit.integration.junit4.JUnit4ClassRunnerDecorator
aaa

也许你在嘲笑hibernate的保存方法?如果是这样尝试使用jmockit的hibernate3emul.jar

答案 2 :(得分:0)

我认为您的问题可能是您的通用类型签名与您尝试模拟的方法不匹配。请记住,泛型类型信息在运行时不可用,因此JMockit只能看到擦除泛型类型信息所产生的方法。

例如,假设您有以下签名:

public <T> void foo(T param) {
  ...
}

编译器将其转换为:

public void foo(Object param) {
  ...
}

这是字节码中出现的签名,以及JMockit通过反射看到的内容。

相比之下,签名如下:

public <T extends Map> foo(T param) {
  ...
}

擦除:

public void foo(Map param) {
  ...
}

因此,例如,如果您的模拟实现在真实类使用第二个签名时使用第一个签名,则基础擦除将不匹配,并且JMockit将无法找到该方法。

有关详细信息,请参阅http://today.java.net/pub/a/today/2003/12/02/explorations.html

答案 3 :(得分:0)

也许你有同样的星座:

我尝试在<T> T merge(T t)中模拟方法org.hibernate.ejb.EntityManagerImpl并得到相同的Mockit错误。

Mockit.redefineMethods(EntityManagerImpl.class, EntityManagerImplMock.class);

我的错误是合并方法未在org.hibernate.ejb.EntityManagerImpl中声明,而是在其超类AbstractEntityManagerImpl中声明。现在我有了

Mockit.redefineMethods(AbstractEntityManagerImpl.class, EntityManagerImplMock.class);

public static class EntityManagerImplMock {
    public <T> T merge(final T o) {
        // nothing
        return o;
    }
}

并且一切正常。