由于方法在使用泛型方法

时间:2016-06-10 14:57:26

标签: java unit-testing generics mockito

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

服务层的示例设置如下:

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(),然后调用{{1}在deleteAllItems()上。在我实现Java泛型之前,这很好用,但是由于我已经实现了Java泛型,因此Mockito测试失败并出现以下异常:

  

java.lang.NoSuchMethodError:   Service.deleteAllItems(实体;)V     在   Test.test(Test.java:XXX)   org.mockito.exceptions.misusing.UnfinishedVerificationException:   缺少方法调用验证(模拟):    - &GT;在Test.test(Test.java:XXX)

     

正确验证的示例:       验证(模拟).doSomething()

     

此外,此错误可能会显示,因为您验证以下任一项:   final / private / equals()/ hashCode()方法。那些方法不能   存根/验证。

听起来它无法找到方法。我应该嘲笑Service的抽象类还是还有其他我缺少的东西?

修改

从我在Mockito内部工作中所看到的情况来看,它创造了一个这样的例子:

AbstractService

对于public void AbstractService.deleteAllItems(Entity) 对象,因此MockitoMethod&#34;不被称为&#34;是有道理的,看来Mockito假定只调用了基类。所以看来我需要模拟基类。我将进一步调查,但如果有人有任何其他想法,我愿意接受建议

3 个答案:

答案 0 :(得分:1)

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

@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 :(得分:0)

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

Java创建这些方法是因为子类的方法不是通用的,因此它们需要“看起来像”非泛型方法(即不受擦除,反射将按预期工作,等等)。有关详细信息,请参阅this answer

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

答案 2 :(得分:0)

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

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并解决我们的问题。

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