日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

单元测试线程代码的5个技巧

發布時間:2023/12/3 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 单元测试线程代码的5个技巧 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這是一些技巧,說明如何進行代碼的邏輯正確性測試(與多線程正確性相對)。

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

  • 面向任務–許多短期運行的同類任務,通常在Java 5執行程序框架內運行,
  • 面向流程–很少,長時間運行的異構任務,通常基于事件(等待通知)或輪詢(周期之間休眠),通常使用線程或可運行的方式表示。
  • 測試這兩種類型的代碼可能很難。 該工作是在另一個線程中完成的,因此完成通知可能是不透明的,或者隱藏在抽象級別的后面。

    該代碼在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());} }

    這有優點和缺點:

    優點:

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

  • 偵聽器代碼可能很復雜,并且會帶來自己的問題,從而創建了應測試的其他生產代碼。
  • 將提交與通知分離。
  • 要求您處理沒有發送通知的情況(例如由于錯誤)。
  • 測試代碼可能很冗長,因此容易出錯。
  • 參考: Alex Collins博客博客中來自JCG合作伙伴 Alex Collins的5條關于單元測試線程代碼的技巧 。


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

    總結

    以上是生活随笔為你收集整理的单元测试线程代码的5个技巧的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。