2015-02-10 115 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>