單元測試線程代碼的5個技巧

這是一些技巧,說明如何進行代碼的邏輯正確性測試(與多線程正確性相對)。

我發現本質上有兩種帶有線程代碼的刻板印象模式:

  1. 面向任務–許多短期運行的同類任務,通常在Java 5執行程序框架內運行,
  2. 面向流程–很少,長時間運行的異構任務,通常基于事件(等待通知)或輪詢(周期之間休眠),通常使用線程或可運行的方式表示。

測試這兩種類型的代碼可能很難。 該工作是在另一個線程中完成的,因此完成通知可能是不透明的,或者隱藏在抽象級別的后面。

該代碼在GitHub上 。

提示1 –生命周期管理對象

具有生命周期受管理的對象更易于測試,該生命周期允許設置和拆卸,這意味著您可以在測試后進行清理,而沒有亂碼干擾任何其他測試。

public class Foo {private ExecutorService executorService;public void start() {executorService = Executors.newSingleThreadExecutor();}public void stop() {executorService.shutdown();}
}

技巧2 –設置測試超時

代碼中的錯誤(如下所示)可能導致多線程測試永遠不會完成,例如(例如)您正在等待從未設置的標志。 JUnit允許您設置測試超時。

... 
@Test(timeout = 100) // in case we never get a notification 
public void testGivenNewFooWhenIncrThenGetOne() throws Exception { 
...

技巧3 –在與測試相同的線程中運行任務

通常,您將擁有一個在線程池中運行任務的對象。 這意味著您的單元測試可能必須等待任務完成,但是您不知道什么時候完成。 您可能會猜測,例如:

public class Foo {private final AtomicLong foo = new AtomicLong();
...public void incr() {executorService.submit(new Runnable() {@Overridepublic void run() {foo.incrementAndGet();}});}
...public long get() {return foo.get();}
}
public class FooTest {private Foo sut; // system under test@Beforepublic void setUp() throws Exception {sut = new Foo();sut.start();}@Afterpublic void tearDown() throws Exception {sut.stop();}@Testpublic void testGivenFooWhenIncrementGetOne() throws Exception {sut.incr();Thread.sleep(1000); // yuk - a slow test - don't do thisassertEquals("foo", 1, sut.get());}
}

但這是有問題的。 執行是不統一的,因此不能保證它可以在另一臺機器上運行。 它很脆弱,對代碼的更改可能會導致測試失敗,因為它突然花費了太長時間。 它的速度很慢,因為當它失敗時您會大方入睡。

一個訣竅是使任務同步運行,即與測試在同一線程中運行。 這可以通過注入執行程序來實現:

public class Foo {
...public Foo(ExecutorService executorService) {this.executorService = executorService;}
...public void stop() {// nop
}

然后,您可以使用同步執行程序服務(概念類似于SynchronousQueue)進行測試:

public class SynchronousExecutorService extends AbstractExecutorService {private boolean shutdown;@Overridepublic void shutdown() {shutdown = true;}@Overridepublic List<Runnable> shutdownNow() {shutdown = true; return Collections.emptyList();}@Overridepublic boolean isShutdown() {shutdown = true; return shutdown;}@Overridepublic boolean isTerminated() {return shutdown;}@Overridepublic boolean awaitTermination(final long timeout, final TimeUnit unit) {return true;}@Overridepublic void execute(final Runnable command) {command.run();}
}

不需要睡覺的更新測試:

public class FooTest {private Foo sut; // system under testprivate ExecutorService executorService;@Beforepublic void setUp() throws Exception {executorService = new SynchronousExecutorService();sut = new Foo(executorService);sut.start();}@Afterpublic void tearDown() throws Exception {sut.stop();executorService.shutdown();}@Testpublic void testGivenFooWhenIncrementGetOne() throws Exception {sut.incr();assertEquals("foo", 1, sut.get());}
}

請注意,您需要從外部對Foo的執行程序進行生命周期管理。

技巧4 –從線程中提取工作

如果您的線程正在等待一個事件,或者正在等待某個時間,則將其提取到自己的方法中并直接調用它。 考慮一下:

public class FooThread extends Thread {private final Object ready = new Object();private volatile boolean cancelled;private final AtomicLong foo = new AtomicLong();@Overridepublic void run() {try {synchronized (ready) {while (!cancelled) {ready.wait();foo.incrementAndGet();}}} catch (InterruptedException e) {e.printStackTrace(); // bad practise generally, but good enough for this example}}public void incr() {synchronized (ready) {ready.notifyAll();}}public long get() {return foo.get();}public void cancel() throws InterruptedException {cancelled = true;synchronized (ready) {ready.notifyAll();}}
}

而這個測試:

public class FooThreadTest {private FooThread sut;@Beforepublic void setUp() throws Exception {sut = new FooThread();sut.start();Thread.sleep(1000); // yukassertEquals("thread state", Thread.State.WAITING, sut.getState());}@Afterpublic void tearDown() throws Exception {sut.cancel();}@Afterpublic void tearDown() throws Exception {sut.cancel();}@Testpublic void testGivenNewFooWhenIncrThenGetOne() throws Exception {sut.incr();Thread.sleep(1000); // yukassertEquals("foo", 1, sut.get());}
}

現在提取工作:

@Overridepublic void run() {try {synchronized (ready) {while (!cancelled) {ready.wait();undertakeWork();}}} catch (InterruptedException e) {e.printStackTrace(); // bad practise generally, but good enough for this example}}void undertakeWork() {foo.incrementAndGet();}

重構測試:

public class FooThreadTest {private FooThread sut;@Beforepublic void setUp() throws Exception {sut = new FooThread();}@Testpublic void testGivenNewFooWhenIncrThenGetOne() throws Exception {sut.incr();sut.undertakeWork();assertEquals("foo", 1, sut.get());}
}

提示5 –通過事件通知狀態更改

前面兩個技巧的替代方法是使用通知系統,以便您的測試可以偵聽線程對象。

這是一個面向任務的示例:

public class ObservableFoo extends Observable {private final AtomicLong foo = new AtomicLong();private ExecutorService executorService;public void start() {executorService = Executors.newSingleThreadExecutor();}public void stop() {executorService.shutdown();}public void incr() {executorService.submit(new Runnable() {@Overridepublic void run() {foo.incrementAndGet();setChanged();notifyObservers(); // lazy use of observable}});}public long get() {return foo.get();}
}

及其對應的測試(注意使用超時):

public class ObservableFooTest implements Observer {private ObservableFoo sut;private CountDownLatch updateLatch; // used to react to event@Beforepublic void setUp() throws Exception {updateLatch = new CountDownLatch(1);sut = new ObservableFoo();sut.addObserver(this);sut.start();}@Overridepublic void update(final Observable o, final Object arg) {assert o == sut;updateLatch.countDown();}@Afterpublic void tearDown() throws Exception {sut.deleteObserver(this);sut.stop();}@Test(timeout = 100) // in case we never get a notificationpublic void testGivenNewFooWhenIncrThenGetOne() throws Exception {sut.incr();updateLatch.await();assertEquals("foo", 1, sut.get());}
}

這有優點和缺點:

優點:

  1. 創建用于偵聽對象的有用代碼。
  2. 可以利用現有的通知代碼,這使其成為已經存在的一個不錯的選擇。
  3. 更加靈活,可以同時應用于任務和面向過程的代碼。
  4. 它比提取工作更具凝聚力。

缺點:

  1. 偵聽器代碼可能很復雜,并且會帶來自己的問題,從而創建了應測試的其他生產代碼。
  2. 將提交與通知分離。
  3. 要求您處理沒有發送通知的情況(例如由于錯誤)。
  4. 測試代碼可能很冗長,因此容易出錯。

參考: Alex Collins博客博客中來自JCG合作伙伴 Alex Collins的5條關于單元測試線程代碼的技巧 。


翻譯自: https://www.javacodegeeks.com/2012/09/5-tips-for-unit-testing-threaded-code.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/371521.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/371521.shtml
英文地址,請注明出處:http://en.pswp.cn/news/371521.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

jsp2

D:\Software\Tomcat7\work\Catalina\localhost 是緩存目錄&#xff0c;可以刪掉隱藏域&#xff1a;頁面表單中的一個元素&#xff0c;跟文本框一樣&#xff0c;但是用戶看不到1.建立test1--form表單需要它&#xff0c;而不需要用戶看到&#xff0c;用隱藏域<body><%re…

MongoDB MapReduce 的示例。

// JavaScript source code db.runCommand({mapreduce: "page",map: function Map() {emit(this.title, // how to group{ name: this.name } // associated data point (document));},reduce: function Reduce(key, values) {//reduce用來處理group出來是多條數…

c語言長空格的代碼是什么,c語言中表示空格的是什么代碼?

分析如下&#xff1a;不是所有字符都需要轉義的&#xff0c;空格直接就敲空格&#xff0c;或者使用ASCII碼值賦值為32。空格沒有轉義字符。合法轉義字符如下&#xff1a;\a 響鈴(BEL) 、\b 退格(BS)、\f 換頁(FF)、\n 換行(LF)、\r 回車(CR)、\t 水平制表(HT)、\v 垂直制表(VT)…

使用NoSQL實現實體服務–第1部分:概述

在過去的幾周中&#xff0c;我一直在進行一些研發工作&#xff0c;以了解使用NoSQL數據庫實現實體服務 &#xff08;也稱為數據服務&#xff09;的優勢。 實體服務是托馬斯埃爾&#xff08;Thomas Erl&#xff09;的《服務技術》叢書中提出的服務分類。 它用于描述高度不可知和…

IO注意事項

read()方法返回值為什么是int? 因為字節輸入流可以操作任意類型的文件,比如圖片音頻等,這些文件底層都是以二進制形式的存儲的,如果每次讀取都返回byte,有可能在讀到中間的時候遇到111111111,那么這11111111是byte類型的-1,我們的程序是遇到-1就會停止不讀了,后面的數據就讀不…

c語言用星號輸出沙漏,《算法筆記》學習日記——3.3 圖形輸出

3.3 圖形輸出問題 A: 輸出梯形題目描述輸入一個高度h&#xff0c;輸出一個高為h&#xff0c;上底邊為h的梯形。輸入一個整數h(1<h<1000)。輸出h所對應的梯形。樣例輸入web5樣例輸出數組*********************************************思路這一類的題目都比較簡單&#xf…

JavaOne 2012:101種改進Java的方法-開發人員參與為何如此重要

Bruno Souza &#xff0c; Martijn Verburg和Heather Vancura在希爾頓酒店的大陸宴會廳4中展示了“ 101種改進Java的方法&#xff1a;開發人員參與為何如此重要”。 他們將其分為自己最熟悉的領域。 SouJava的創始人兼協調員 Souza談到了通過用戶組的更大參與。 Verberg也在倫敦…

Java組合實體模式~

組合實體模式用于EJB持久化機制。 組合實體是表示對象圖的EJB實體bean。 當組合實體更新時&#xff0c;內部依賴對象bean將自動更新為由EJB實體bean管理。 以下是組合實體Bean的參與者。 組合實體 - 它是主要的實體bean。 它可以是粗粒度的或可以包含用于持久性目的的粗粒度對象…

python中的一些小知識

在最近學習python中遇到的一些小問題匯總一下&#xff1a; 1.在windows7下安裝python3.5版本時提示安裝不了&#xff0c;缺少ServicePack1. 解決辦法是&#xff0c;打開控制面板\系統和安全\Windows Update&#xff0c;下載和更新計算機安裝&#xff0c;然后卸載以前的python版…

在Java中衡量執行時間– Spring StopWatch示例

有兩種方法可以通過使用System.currentTimeinMillis&#xff08;&#xff09;或通過使用System.nanoTime&#xff08;&#xff09; 來測量Java中經過的執行時間 。 這兩個方法可用于測量 Java中兩個方法調用或事件之間的經過時間或執行時間 。 計算經過的時間是Java程序員要做的…

c語言getch在哪個頭文件,用getch()需要頭文件嗎?

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓#include #include #include "string.h"#includeusing namespace std;struct student{ int num;char name[10];char banji[10];float score[3];struct student *next;};struct student *creat(){struct student *head,*p…

My solution for Git Client Error: Permission denied (publickey)

在使用Git客戶端的過程中遇到的問題以及解決方案分享。 我之前已經安裝Git客戶端并且使用Git開發過公司項目&#xff0c;也已經正確生成PublicKey并且添加到SSH keys on github of my account&#xff0c;但是當我想從github上克隆另一個客戶端push的代碼的時候一直報錯&#x…

OutOfMemoryError:無法創建新的本機線程–問題神秘化

正如您從我以前的教程和案例研究中可能已經看到的那樣&#xff0c;要確定和解決Java Heap Space OutOfMemoryError問題可能很復雜。 我從Java EE生產系統中觀察到的常見問題之一是OutOfMemoryError&#xff1a;無法創建新的本機線程&#xff1b; HotSpot JVM無法進一步創建新的…

求10以內平均數的c語言,求助 給小學生出題,自己選加減乘除 做10題 10以內的數 然后統計分...

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓#include #include #include void Menu(void){printf("1,加法 2,減法 3,乘法 4,除法 5,退出\n");printf("請選擇題目類型:");}int Plus(void){int a, b;a rand() % 10 1;b rand() % 10 1;printf("%-2…

linux常用命令大全(轉)好東西要分享

1、ls命令 就是list的縮寫&#xff0c;通過ls 命令不僅可以查看linux文件夾包含的文件&#xff0c;而且可以查看文件權限(包括目錄、文件夾、文件權限)查看目錄信息等等 常用參數搭配&#xff1a; ls -a 列出目錄所有文件&#xff0c;包含以.開始的隱藏文件 ls -A 列出除.及.…

Cobertura和Maven:集成和單元測試的代碼覆蓋率

在姜黃項目中&#xff0c;我們每晚維護一個儀表板。 在儀表板上&#xff0c;我們收集有關項目的統計信息&#xff0c;包括代碼覆蓋率&#xff0c;findbugs分析和其他指標。 我們一直在使用Maven EMMA插件來提供代碼覆蓋&#xff0c;但是遇到了EMMA問題。 在對類進行檢測后&…

二分圖之匈牙利算法模版

1 /*2 匈牙利算法模版鄰接表版3 最大匹配問題4 時間復雜度&#xff1a;O (nm)5 */6 #include <cstdio>7 #include <vector>8 #include <cstring>9 using namespace std; 10 const int maxn 505; 11 vector<int> v[maxn];//x v[i][j]表示i可以與x匹配…

android 字體描邊實現,android文字描邊功能的實現

這里也要簡單說一下&#xff0c;這些小模塊并不是我原創&#xff0c;也是當時查資料找到的&#xff0c;由于時間比較久&#xff0c;原文鏈接已經忘記了&#xff0c;所以這里就不列出引用鏈接了。不過這些代碼我都修改、完善過&#xff0c;也添加了一些注釋&#xff0c;希望對大…

Factorial vs Power

題意 輸入a&#xff0c;找到滿足n!>a^n 最小的n。 數據 第一行T(1 < T < 1e5)&#xff0c;表示測試樣例數.(2 < a < 1e6)。 輸入 3 2 3 4 輸出 4 7 9 這個東西一看就知道是二分求解的&#xff0c;但是我們還是不知道怎么求的&#xff0c;我們可以吧他們取對數然…

評論:Arun Gupta撰寫的“ Java EE 6 Pocket Guide”

這是我很高興寫的評論。 我的朋友阿倫&#xff08;Arun&#xff09;發布了Java EE 6袖珍指南&#xff0c;該指南將在您訂購時盡早提供。 我很早就知道這本書&#xff0c;因為我很樂意對其進行回顧&#xff0c;也感謝有機會為本書做出一點貢獻&#xff01; Kindle版本已經可用&a…