2013-07-15 27 views
1

方法時,但我們也需要開發單元測試。的Mockito:顯示java.lang.NullPointerException調用從嘲笑我使用到的Mockito寫上已與集成測試測試應用一些單元測試

這是測試代碼:

public class TestResourceB { 

@Mock 
ResourceB b; 
@Mock 
ResourceC c; 

@Before 
public void setup() { 
    MockitoAnnotations.initMocks(this); 
    TestObjects.InitializeObjects(); 
} 

@Test 
public void testMethodResourceA() { 
    when(b.callFuncA()).thenCallRealMethod(); 
    when(b.callFuncB()).thenReturn(TestObjects.mockedListA); 
    when(b.callFuncC((B)anyVararg())).thenCallRealMethod(); 
    when(c.callFuncB()).thenReturn(TestObjects.mockedListB); 
    when(c.callFuncA()).thenCallRealMethod 
    String output = b.callFuncA(); 
} 
} 

這是類ResourceB

public class ResourceB { 

ResourceC c = new ResourceC(); 

public String callFuncA(){ 
    /*Calling callFuncB and doing some other stuff*/ 
    String test = callFuncC(arg1); 
} 

public List<A> callFuncB() { 
    /*returns the mocked list A*/ 
} 

public String callFuncC(B arg1) { 
String test2 = c.callFuncA(); // It crashes here 
/*doing some other stuff*/ 
} 
} 

這是類ResourceC

public class ResourceC { 
public String callFuncA() { 
    /*Calling callFuncB and doing some other stuff*/ 
    return testString; 
} 

public List<B> callFuncB() { 
/*return a List*/ 
} 
} 

,我有是問題在類ResourceB中的方法callFuncC中當行

String test2 = c.callFuncA(); 

叫我得到一個NullPointerException

任何想法,這可能是爲什麼發生?

回答

6

有幾個問題在你的代碼,第一個是你的嘲諷你要測試的類,這樣做,你只會測試ResourceB模擬或你有存根的代碼和力選擇的方法調用實際代碼(thenCallRealMethod)。主要想法是從來沒有模擬你正在測試的類。

這也是爲什麼你有一個NPE,因爲一個模擬不需要內部字段的實例。因爲它不應該。

下面是一個正確的方法,可能會有變化,但一個是最直接的。所以基本上你想測試ResourceBResourceC之間的相互作用,因爲這是你想要嘲笑ResourceCResourceB單元測試。事情是模擬考試是每個實例,所以你必須在嘲笑一個傳遞到ResourceB

它可以通過構造函數注入注入,然後需要修改ResourceB

public class ResourceB { 
    ResourceC c; 

    public ResourceB() { c = new resourceC(); } // normal behavior 

    @VisibleForTesting // guava annotation (in, order to expalain why there is this constructor) 
    ResourceB(ResourceC c_override) { c = c_override; } // constructor for the test, note the package visibility 

    // ... 
} 

而且在測試中,你會這樣寫:

public class TestResourceB { 
    ResourceB tested_b; 
    @Mock ResourceC mocked_c; 

    @Before 
    public void init_tested_and_mocks() { 
    MockitoAnnotations.initMocks(this); 
    tested_b = new ResourceB(mocked_) 
    } 

    @Test 
    public void ensure_result_from_ResourceC_is_returned() { 
    // given 
    when(mocked_c.callFuncA()).thenReturn("result that should be returned"); 

    // when 
    String output = tested_b.callFuncA(); 

    // then 
    assertThat(output).isEqualTo("result that should be returned"); 
    } 
} 

順便說這裏的有幾件事要補充:

  • 當你使用JUnit 4.x時,我使用更有意義/描述性的方法名。
  • 我使用行爲驅動開發關鍵字(給出然後),以幫助推動測試場景。
  • 另外我用AssertJ lib來寫出有意義的斷言。
+0

,我嘲笑ResourceB類的原因是,我需要模擬一個數據庫的交互,其發生內部callFuncB()在ResourceB。 是否有可能嘲笑這個函數的結果沒有嘲諷類ResourceB? – SteveSt

+1

@Stefanos我強烈建議你不要這樣做。在mockito中,我們有'間諜',但他們是非常特殊的情況。大多數時間間諜導致代碼沒有正確測試,這可能導致錯誤的安全感。如果你想用數據庫測試一些東西,你應該寫一個**集成測試**,它應該連接真實的數據庫,或者至少像H2這樣的東西。即使使用嵌入式數據庫也存在風險,因爲您無法真正依賴它的實現來模擬數據庫。 – Brice

+0

非常感謝您的建議:) – SteveSt