2017-08-16 111 views
3

作爲我的單元測試的一部分,我試圖模擬Thread.class isAlive()方法返回true使用Mockito。下面是我的代碼:如何用Mockito模擬Thread.class?

final Thread mockThread = Mockito.mock(Thread.class); 
Mockito.when(mockThread.isAlive()).thenReturn(true); 

這是給我的第二行以下異常:

Exception in thread "main" 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多次這樣沒有​​問題。有嘲笑Thread.class有問題嗎?我已經找遍了,沒有運氣。

+1

'isAlive'是最後的方法,因爲錯誤說你不能存根那些方法 –

回答

2

Thread.isAlive()final修飾符聲明。它不能由1的Mockito
使用Powermock或2的Mockito,增加了this feature被嘲笑:

很長一段時間我們的用戶遭遇了難以置信的Mockito時拒絕 模擬final類。 ... 嘲諷最終的類和方法是孵化,可選的功能

或者另一種方式:改變的方式進行單元測試你的類。

你真的需要依靠isAlive()來進行單元測試嗎?

如果你真的需要它,你也可以使用組成一個Thread實例的包裝類和委託加工的組成Thread
例如,這可以實現Runnable
用這種方法你可以自然地模擬這個包裝類的isAlive()方法。

+0

謝謝,我沒有捕獲isAlive()是最終的,你不能用Mockito模擬最終方法(應讀錯誤更密切)。感謝您的建議。對於我的測試用例,我添加了一個只在同步塊中調用wait()的私有Runnable類。這樣,我可以調用並使isAlive()成爲true,並通過調用notify()來終止該線程。再次感謝您的快速回答。 –

+1

歡迎您:)我編輯了關於Mockito 2的解答。您的解決問題的方式似乎很有趣。不要猶豫,隨時添加答案。它可以幫助其他人。 – davidxxx

+0

感謝您的建議,很好的電話我會做 –

1

它看起來像Thread.isAlive方法是final,最終的方法不能用Mockito模擬。

這是related stackoverflow post

+0

謝謝,引用的帖子有一個非常有用的解釋PowerMock的解決方案。 –

+1

更正:最新版本的Mockito ** do **支持模擬最終的類/方法。它是實驗性的,但我仍然喜歡實驗Mockito而不是普通的PowerMock(ito)。 – GhostCat

2

正如其他人指出的,Thread的isAlive方法是final的,因此不能使用Mockito來模擬(也可能不應該)。

一個簡單的解決方法是使用只調用this.wait()的run()方法在我的單元測試類中創建一個私有的Runnable類。

private class TestRunnable implements Runnable { 
     @Override 
     public void run() { 
      synchronized(this) { 
       try { 
        this.wait(); 
       } catch (InterruptedException e) { 
        System.out.println("Interrupted!"); 
       } 
      }  
     } 
    } 

我就能夠創建一個使用了這樣的一個實例,並進行IsAlive()方法將總是返回真值在單元測試中的線程。

final TestRunnable testRunnable = new TestRunnable(); 
final Thread expectedThread = new Thread(testRunnable); 
expectedThread.start(); 

然後在測試中。

assertTrue(expectedThread.isAlive()); 

最後,要清理線程,只需調用notify()上Runnable的實例(當然,當JUnit的完成也無妨線程將結束)

synchronized(testRunnable) { 
    testRunnable.notifyAll(); // kill the Runnable (prob not necessary) 
} 
+1

你存有'線程'。好:) – davidxxx

+2

一個新手的好答案!就我個人而言,我只是做'新線程(() - > {while(true){}});' – Michael

+0

@邁克爾我一開始就以爲你。但通過考慮它,單元測試必須儘可能快地執行。在某些情況下,我們甚至可能會將它們並行化。 'wait()'效率更高,因爲它不會消耗CPU核心/線程,所以我認爲它應該在繁忙的等待中受到青睞。 – davidxxx

2

我給你一個完全不同的透視:擺脫與您的產品代碼中的線程完全相互影響

事情是:線程是一個漂亮的低級別多線程的抽象。 10,15年前我們只有線程,並因此被迫使用它們。

但現在,你有一個巨大的各種抽象是在更高層次上工作,像ExecutorServicesFutures,並承諾,...

這聽起來很奇怪 - 但它可能是更有助於您退後一步,考慮而不是按照您今天使用它們的方式使用線程。你看,有些東西就像Same-Thread-Executors,它們允許你把你的多線程代碼變成單個的線程進行測試 - 只需提供一個不同的服務來執行任務。

這就是2017年應該爭取的抽象級別 - 您不應該浪費時間嘲弄低級別的最終方法!

+0

@davidxxx我同意。但是很難給出一個具體的例子 - 考慮到OP只詢問Thread.alive() - 他沒有讓我們瞭解他的生產代碼 - 並且已經有了一個很好的,被接受的答案。那麼發明人造的例子並沒有多少意義。 – GhostCat

+1

你沒有錯。也許,它會給下一個問題的想法,你會這樣一個場合來說明一個具體的情況:) – davidxxx

+0

@GhostCat感謝您的洞察力。我起初考慮避免直接使用Thread.class,因爲你提到的原因,但經過更多思考後,我決定使用它。我的使用案例是我與限制請求/分鐘和請求/天的API進行交互,並因超出這些限制而收取「超額」費用,因此客戶必須跟蹤其自身使用情況。問題是,'第一天'或'分鐘'直到第一次請求才會啓動。這排除了任何調度程序服務。我發現使用同步/阻塞直接使用線程可以更容易地節制/跟蹤剩餘容差。 –