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

单元测试怎么测试线程

以下是一些技巧,说明如何进行代码的逻辑正确性测试(与多线程正确性相对)。

我发现本质上有两种带有线程代码的刻板印象模式:

  1. 面向任务-许多短期运行的同类任务,通常在Java 5执行程序框架内运行,
  2. 面向流程–很少,长时间运行的异构任务,通常基于事件(等待通知)或轮询(周期之间Hibernate),通常使用线程或可运行的方式表示。

测试这两种类型的代码可能很难。 该工作是在另一个线程中完成的,因此完成的通知可能是不透明的,或者隐藏在抽象级别的后面。

该代码在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.mzph.cn/news/353785.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UBUNTU下双显示器设置

UBUNTU下双显示器设置 (2010-05-08 17:31) 分类&#xff1a; linux ubuntu&#xff08;GNOME&#xff09;现在已经能很好的处理双屏了&#xff0c;无论是克隆方式还是扩展方式&#xff01;   但有时我们需要一个不同的管理器如awesome、fluxbox这类简单的窗口管理器中又如何设…

结对第二次作业

题目要求 我们在刚开始上课的时候介绍过一个小学四则运算自动生成程序的例子&#xff0c;请实现它&#xff0c;要求&#xff1a; 能够自动生成四则运算练习题可以定制题目数量用户可以选择运算符用户设置最大数&#xff08;如十以内、百以内等&#xff09;用户选择是否有括号、…

JavaFX缺少的功能调查:CSS

在“ 缺少的功能调查”系列的最后一篇文章中&#xff0c;我说过这篇文章是关于CSS和FXML中缺少的功能。 现在事实证明&#xff0c;调查提交的内容不包含任何有效的FXML问题。 因此&#xff0c;我将仅关注CSS。 这些是报告CSS功能丢失&#xff1a; 完全CSS支持–当前JavaFX CS…

JAVA程序员面试题集合

JAVA程序员面试题集合 分类&#xff1a; 编程语言 2012-12-08 12:10 50人阅读 评论(0) 收藏 举报 1&#xff0e;面向对象的特征有哪些方面(1)抽象&#xff1a;抽象就是忽略一个主题中与当前目标无关的那些方面&#xff0c;以便更充分地注意与当前目标有关的方面。抽象并不打算…

STM32F105 USB管脚Vbus的处理

源&#xff1a;STM32F105 USB管脚Vbus的处理 对于STM32F105/107来说&#xff0c;为了监测USB的连接问题&#xff0c;程序默认是通过Vbus管脚进行检查的。但是Vbus管脚和UART1的TXD复用&#xff0c;导致我们在使用UART1发送数据时候&#xff0c;USB重启的问题。为了解决这个问题…

Spy++原理初探

Spy原理初探 http://www.vckbase.com/index.php/wv/1480.html文章概要&#xff1a;用Visual Studio搞开发的朋友对Spy这个工具一定不陌生&#xff0c;它可以分析窗体结构、进程和窗口消息&#xff0c;对开发工作有很大辅助作用。我们需要研究某个对象时&#xff0c;只要调出其…

gradle ant_使用Gradle引导旧式Ant构建

gradle antGradle提供了几种不同的方式来利用您在Ant上的现有投资&#xff0c;无论是积累的知识还是您已经放入构建文件的时间。 这可以极大地方便将Ant生成的项目移植到Gradle的过程&#xff0c;并为您提供逐步进行此操作的路径。 Gradle文档在描述如何在Gradle构建脚本中使用…

confluence 为合并的单元格新增一行

1&#xff0c;先将最后一个结构取消合并单元格 | | ___ | | | ___ | | _ | ___ | 2&#xff0c;在最后一行追加一行&#xff0c;将左侧合并 3&#xff0c;将上面取消合并的重新合并即可转载于:https://www.cnblogs.com/lavin/p/8866867.html

java怎么把system.out的东西输出到文件上

java怎么把system.out的东西输出到文件上 浏览(1836)|评论(0) 交流分类&#xff1a;Java|笔记分类: 未分类 RT&#xff0c;我们在程序里system.out的东西都是从控制台刷过。 如果你想它写到文件里&#xff0c;以下是一种解决方法&#xff0c;当然你还可以用log4j java代码…

纯Java中的Functor和Monad示例

本文最初是我们使用RxJava进行反应式编程的附录。 但是&#xff0c;尽管与反应式编程非常相关&#xff0c;但对monad的介绍却不太适合。 因此&#xff0c;我决定将其取出并作为博客文章单独发布。 我知道&#xff0c;“ 我自己的&#xff0c;对单子的一半正确和半完全的解释 ”…

[NOI2012]美食节

题解&#xff1a; 很经典的网络流 对于每个厨师拆点分开统计 1倍 2倍 3倍 n&#xff08;mp)^2 有点大 动态加边 即对于每个厨师有了i才会有i1 不过好像还是有点卡常&#xff1f;&#xff1f; 代码&#xff1a; #include <bits/stdc.h> using namespace std; #define INF …

请问:如何实现文件日志功能?要求每天换一个文件。文件名以日期区分

结帖率&#xff1a;100%#1 得分&#xff1a;0 回复于&#xff1a; 2004-06-22 17:24:13 使用一个单独的类封装日志记录&#xff0c;在该类中记录当前使用的文件名&#xff0c;每次记日志时获取一下系统时间&#xff0c;与文件名匹配一下&#xff0c;发现日期不同则新开文件。下…

微信小程序的scroll-view组件

scroll-view为滚动视图&#xff0c;共有水平滚动和垂直滚动两种使用竖向滚动时&#xff0c;需要给<scroll-view/>一个固定高度&#xff0c;通过 WXSS 设置 height。index.wxss 是页面的结构文件&#xff1a;<!--垂直滚动--> <view class"section">…

eclipse使用技巧_有效使用Eclipse的热门技巧

eclipse使用技巧以下是一些技巧&#xff0c;可以帮助您避免潜在的问题并在使用Eclipse时提高工作效率。 避免安装问题 切勿在旧版本之上安装新版本的Eclipse。 首先重命名旧版本&#xff0c;以将其移开&#xff0c;然后将新版本解压缩到干净的目录中。 恢复混乱的工作空间 …

日志文件的编写

/// <summary> /// 写入日志到文本文件 /// </summary> /// <param name"action">动作</param> /// <param name"strMessage">日志内容</param> /// <param name"time">时间</param> pub…

MFC非模态对话框实例

【转载】MFC非模态对话框实例 2012-06-17 16:21:41| 分类&#xff1a; C/MFC | 标签&#xff1a; |字号大中小 订阅 实例目的 在Windows环境中&#xff0c;对话框是一种常用的输入输出手段。对话框有两种类型&#xff0c;非模态和模态。非模态对话框与模态对话框不同&a…

Node简单服务器开发

运用的知识&#xff1a;http&#xff0c;fs&#xff0c;get&#xff0c;post 接口定义&#xff1a;/user?actreg$useraaa&passbbb后台返回格式&#xff1a;{"ok":false,"msg":"原因"}/user?actlogin$useraaa&passbbb后台返回格式&…

5个常见的Hibernate异常及其解决方法

了解如何使用Hibernate轻松解决最常见的问题 Hibernate可能是市场上最受欢迎的JPA实现&#xff0c;您可以在许多地方看到它&#xff0c;例如&#xff1a; 您自己使用过的项目数&#xff0c; 需要Hibernate经验的职位数量&#xff0c;当然还有 互联网上发布的问题和例外数量…

MATLAB figure中提取数据

MATLAB figure中提取数据 (2011-10-26 14:26:21) 转载▼标签&#xff1a; 杂谈 分类&#xff1a; matlab figure画出来&#xff0c;提取数据有很多好处&#xff0c;方便保存&#xff0c;计算&#xff0c;加工&#xff0c;还可以导入到origin里面画图。具体的方法就是两部。第一…

Wine下完美安装QQ 2010:

Wine下完美安装QQ 2010&#xff1a;1、先安装Wine&#xff1a;sudo apt-get install wine2、获取获取最新的WineTricks&#xff0c;WineTricks 是 Wine的其中一个开发者DanKegel写的一个安装Windows有关库和软件的小程序&#xff0c;非常有用。下载WineTricks&#xff0c;在终端…