2015-02-10 94 views
2

我想单元测试使用com.basho.riak的代码:riak-client:2.0.0。我嘲笑所有的riak客户端类,并希望得到一个无用的,但工作测试。然而,这种失败,一个空指针:如何模拟riak java客户端?

java.lang.NullPointerException 
    at com.basho.riak.client.api.commands.kv.KvResponseBase.convertValues(KvResponseBase.java:243) 
    at com.basho.riak.client.api.commands.kv.KvResponseBase.getValue(KvResponseBase.java:150) 
    at com.basho.riak.client.api.commands.kv.FetchValue$Response.getValue(FetchValue.java:171) 

我的测试是这样的:

@Test public void test() {   
     RiakClient riakClient = mock(RiakClient.class); 

     @SuppressWarnings("unchecked") 
     RiakCommand<FetchValue.Response, Location> riakCommand = (RiakCommand<FetchValue.Response, Location>) mock(RiakCommand.class); 

     Response response = mock(Response.class); 
     when(riakClient.execute(riakCommand)).thenReturn(response); 
     Response returnedResponse = riakClient.execute(riakCommand); 

     when(response.getValue(Object.class)).thenReturn(new Object()); 
     MyPojo myData = returnedResponse.getValue(MyPojo.class); 
     // Make assertions 
    } 

如何做到这一点使用了Riak客户端,您的单元测试代码?最终,我想确保使用期望的类型/存储桶/组合键,并且运行预期的RiakCommand。

编辑:我挖了到FetchValue类,发现这个结构:
FetchValue - 是public final

FetchValue.Response
- 为public static
- 有一个包私有构造Response(Init<?> builder)

FetchValue.Response.Init<T>是:
- protected static abstract class Init<T extends Init<T>> extends KvResponseBase.Init<T>

而且有FetchValue.Response.Builder
static class Builder extends Init<Builder>
- 与构建()认为:return new Response(this);

我假设的Mockito获取内部类中某处丢失,我的电话在KvResponseBase.convertValues,其中NP抛出结束。 KvResponseBase.convertValues假设值为List<RiakObject>,我没有看到分配它的理智方式。

回答

1

我已经调查了一下你的情况。我有降低您的例子,这个简单的SSCCE:

import static org.mockito.BDDMockito.given; 
import static org.mockito.Mockito.mock; 
import org.junit.Test; 
import com.basho.riak.client.api.commands.kv.FetchValue.Response; 

public class RiakTest { 
    @Test 
    public void test() throws Exception { 
     Response response = mock(Response.class); 
     given(response.getValue(Object.class)).willReturn(new Object()); 
    } 
} 

会抛出这个错误:

java.lang.NullPointerException 
at com.basho.riak.client.api.commands.kv.KvResponseBase.convertValues(KvResponseBase.java:243) 
at com.basho.riak.client.api.commands.kv.KvResponseBase.getValue(KvResponseBase.java:150) 
at com.basho.riak.client.api.commands.kv.FetchValue$Response.getValue(FetchValue.java:171) 
at RiakTest.test(RiakTest.java:12) 

一些挖后,我想我已经发现的问题。这是你试图存根是从一个包(知名度)继承类一个公共方法:

abstract class KvResponseBase { 
    public <T> T getValue(Class<T> clazz) { 
    } 
} 

看来的Mockito失败,所以真正的一个调用存根这种方法和NullPointerException是抛出(由于访问空成员:values)。需要注意的 一个重要的事情是,如果这个函数调用没有失败,的Mockito会显示相应的错误:

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
    when() requires an argument which has to be 'a method call on a mock'. 
    For example: 
     when(mock.getArticles()).thenReturn(articles); 

    Also, this error might show up because: 
    1. you stub either of: final/private/equals()/hashCode() methods. 
     Those methods *cannot* be stubbed/verified. 
     Mocking methods declared on non-public parent classes is not supported. 
    2. inside when() you don't call method on mock but on some other object. 

我想这是一个的Mockito错误或限制,所以我必须在Mockito tracker打开的问题在那里我用简单的课程重现你的情况。


UPDATE

The issue i opened实际上是一个existing one的副本。这个问题不会被解决,但有一个解决方法。您可以使用Bytebuddy mockmaker而不是cglib。 Explanations可以在这里找到。

+0

已接受。你关于Mockito的发现真的令人印象深刻。你帮助我认识到Mockito不是完成这项任务的完美工具。现在你已经注意到了riakClient的代码,你有什么建议如何测试使用它的代码?谢谢! – Mrtn 2015-02-12 15:48:06

+0

@Mrtn很乐意帮忙。我认为你应该提出一个新问题,揭露你想要测试的东西。我认为你会得到更广泛的帮助,因为它与mockito无关,而是与一般的(单元)测试有关。即使我在Riak的一些课程中看到了一些,我也是一位专家;) – gontard 2015-02-12 15:59:41

0

你不能用mockito模拟final类和final和/或static方法。请注意,static嵌套类很好。这是因为mockito子类(我不是100%肯定这是确切的操作,它使用CGLIB来生成类)对象,但不允许覆盖最终方法或扩展最终类。对于static方法,不可能有重写。

在您的代码中,您可能正在尝试调用最终的类或方法。很难判断哪个类导致了问题,从你的stackstrace中你应该怀疑你嘲笑的第一个对象(从testcase方法开始)。模拟的方法不应该调用任何其他方法(期望内部mockito),所以可能这是最终的,因为你似乎没有称之为“模拟”方法。

在你的情况下堆栈跟踪不完整(因为你的测试用例不在它上面)。在快速查看riak框架时,我无法找到该方法,请看FetchValue$Response.getValue

另请注意以下几点。从你发布的片段中,我不能告诉你在测试用例中测试了什么。你创建的所有对象都是模拟的。通常你有1个(或几个)你正在测试的真实类。其他类(与受测试的类交互)可以模拟复杂行为。

+0

我同意你的答案的大部分,特别是与最后一段:*什么是这个测试的目标*?但我不得不说**你的诊断是错误的**,我已经完成了它的项目的基本步骤,类/方法不是最终的,方法也不是静态的。我还没有发现问题。 – gontard 2015-02-12 09:22:55

+0

我同意,这个测试是毫无意义的。我的宏伟计划是断言我的应用程序将预期的参数传递给了RiakClient。嘲笑和争论匹配似乎是要走的路。现在,感谢@ gontard,我们知道更多,我需要找到另一种解决方案。感谢您的关注和时间! – Mrtn 2015-02-12 14:03:21

0

追问: 感谢@gontard我能找到这样的:

<dependency> 
    <!-- We need this fix: https://github.com/mockito/mockito/pull/171 to use mockito with Riak --> 
<!--http://stackoverflow.com/questions/28442495/how-to-mock-riak-java-client#28474106--> 
    <groupId>org.mockito</groupId> 
    <artifactId>mockito-core</artifactId> 
    <version>2.0.52-beta</version> 
    <scope>test</scope> 
    </dependency> 

这已包含的修复程序。

不幸的是,如果你同时使用Fetch和MultiFetch(很可能),你就会陷入困境。

MultiFetch.Response是一个final类(所以你可以使用的Mockito,你需要使用PowerMock) FetchValue.Response有你列出的问题,也只能尚未固定公测的Mockito,不可用powermock。 ..

更新,我想通了如何结合使用这两种&的Mockito powermock(直到powermock升级):

<!-- We need this to mock Multi-Fetch responses from Riak, which are final --> 
<!-- However, we need the beta version of mockito due to bugs (see below), 
so we _cannot_ use the mockito api provided by powermock, do _not_ include _powermock-api-mockito, it'll mess stuff up --> 
<dependency> 
    <groupId>org.powermock</groupId> 
    <artifactId>powermock-module-junit4</artifactId> 
    <version>1.6.4</version> 
    <scope>test</scope> 
</dependency> 
<!--If we don't include this, we get: --> 
<!--java.lang.IllegalStateException: 
Extension API internal error: org.powermock.api.extension.proxyframework.ProxyFrameworkImpl could not be located in classpath.--> 
<!-- it looks like this is due to some discrepancy in packaging with mockito 2, this may be fixed in Fall 2016: 
https://groups.google.com/forum/#!topic/powermock/cE4T40Xa_wc --> 
<dependency> 
    <groupId>org.powermock</groupId> 
    <artifactId>powermock-api-easymock</artifactId> 
    <version>1.6.4</version> 
</dependency> 


<!-- We need this fix: https://github.com/mockito/mockito/pull/171 to use mockito with Riak --> 
<!--http://stackoverflow.com/questions/28442495/how-to-mock-riak-java-client#28474106--> 
<dependency> 
    <groupId>org.mockito</groupId> 
    <artifactId>mockito-core</artifactId> 
    <version>2.0.52-beta</version> 
    <scope>test</scope> 
    </dependency>