首页 文章

由于方法在使用泛型方法的抽象类中,Mockito在NoSuchMethodError上失败

提问于
浏览
1

我正在进行一些服务测试,我正在测试一个从使用泛型的类扩展的具体类 .

服务层的示例设置如下:

public abstract class AbstractService <E extends AbstractEntity, IT extends AbstractItem> {

    public void deleteAllItems(E entity) {
        List<IT> items = new ArrayList<IT>(entity.getItems());
        for(IT item : items) {
            //Yada, yada
        }
    }
}

public class Service extends AbstractService<Entity, Item> {

}

public class OtherService() {
    @Inject
    private ServiceManager serviceManager;

    public void deleteItems(Entity e) {
        serviceManager.getService().deleteAllItems(e);
    }
}

然后测试它我有以下内容:

public class Test {
    private Service service;
    private OtherService otherService;
    private ServiceManager serviceManager;

    @BeforeMethod
    public void setup() {
        serviceManager= mock(serviceManager.class);
        service= mock(Service.class);
        when(serviceManager.getService()).thenReturn(service);
        otherService=injector.getInstance(OtherService.class);
    }

    @Test
    public void test() {
        Entity e = new Entity();
        //Attach some items
        otherService.deleteItems(e);
        verify(service).deleteAllItems(e);
    }
}

这应该调用 OtherService ,它存在(我们使用注入来获取对象),然后调用方法 deleteItems() ,而后者应该在 Service 上调用 deleteAllItems() . 在我实现Java泛型之前,这很好用,但是由于我已经实现了Java泛型,因此Mockito测试失败并出现以下异常:

java.lang.NoSuchMethodError:Service.deleteAllItems(Entity;)V at Test.test(Test.java:XXX)org.mockito.exceptions.misusing.UnfinishedVerificationException:缺少方法调用验证(mock): - > at Test .test(Test.java:XXX)正确验证的示例:verify(mock).doSomething()此外,此错误可能会显示,因为您验证了:final / private / equals()/ hashCode()方法 . 这些方法无法进行存根/验证 .

听起来似乎无法找到方法 . 我是应该嘲笑 AbstractService 的抽象类还是还有其他我缺少的东西?

EDIT

从我所看到的Mockito内部工作方式来看,它创建了一个这样的实例:

public void AbstractService.deleteAllItems(Entity)

对于 MockitoMethod 对象,这样才有意义 Service.deleteAllItems() "isn't called",看来Mockito假设只调用了基类 . 所以看来我需要模拟基类 . 我对这些建议持开放态度

3 回答

  • 0

    我可以建议将问题本地化 - 要么是在嘲笑:

    @Test
    public void test() {
        Entity e = new Entity();
        service.deleteItems(e); // Note! 'service' itself, not an 'otherService'
        verify(service).deleteAllItems(e);
    }
    

    或者在注入中(删除继承和泛型):

    public class Service /*extends AbstractService<Entity, Item>*/ {
        public void deleteAllItems(Entity entity) {
            //...
        }
    }
    

    迭代地分解问题,你会发现原因 .

  • 1

    当您创建泛型类的非泛型子类时,Java会为使用泛型类型的任何方法创建“桥接方法” . 桥接方法看起来像继承的方法,但使用为泛型参数而不是泛型指定的特定类 .

    Java创建这些方法是因为子类的方法不是通用的,因此它们需要"look like"非泛型方法(即不受擦除,反射将按预期工作,等等) . 有关详细信息,请参见this answer .

    解决方案是让Mockito模拟 serviceManager.getService() 返回的类型 .

  • 0

    经过进一步的调查,我找到了一种方法来迫使Mockito打电话给正确的 class . 正如我简要提到的那样,我们正在使用注射来获得物体 . 在设置过程中,我们确实完成了注射器的设置,我没有感觉到导致问题 . 但它确实提出了一个解决方案 . 这就是我们称之为:

    Injector injector = Guice.createInjector(new Module() {
                @Override
                public void configure(Binder binder) {
                    service = mock(Service.class);
                    binder.bind(Service.class).
                        toInstance(service);
    }
    

    为了解决这个问题,我们只是将 AbstractService 类绑定到 Service 类的模拟实例,如下所示:

    Injector injector = Guice.createInjector(new Module() {
                @Override
                public void configure(Binder binder) {
                    service = mock(Service.class);
                    binder.bind(Service.class).
                        toInstance(service);
                    binder.bind(AbstractService.class).
                        toInstance(service);                    
    }
    

    所以现在,当Mockito尝试获取 AbstractService 的实例时,它会调用模拟的 Service 并解决我们的问题 .

    如果有人有任何反馈它有一个替代解决方案,然后随意发布,我可以测试它,并检查是否有更好的方法,我们正在做什么 .

相关问题