单元测试怎么测试线程

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

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

  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

单元测试怎么测试线程

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

  1. cpu线程_进程/线程上下文切换会用掉你多少CPU?

    进程是操作系统的伟大发明之一,对应用程序屏蔽了CPU调度.内存管理等硬件细节,而抽象出一个进程的概念,让应用程序专心于实现自己的业务逻辑既可,而且在有限的CPU上可以"同时"进行许 ...

  2. java单元测试如何全覆盖_单元测试代码覆盖率的浅谈

    在做单元测试时,代码覆盖率通常被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况.比如,代码覆盖率必须达到80%或90%.于是乎,测试人员费尽心思设计案例覆盖代码,用代码覆盖率来衡 ...

  3. 慕课C 语言第四周单元测试答案,Omics_知到_单元测试答案

    不能并退崩D扳手敲击拧机械晃动.炮钻机钻杆钻C.用采用处理方法,到单杆现打眼夹钻象时出现风钻. 不易唤醒,元测迷C模糊所问.深态是答非的神浅昏昏迷昏睡志状意识,入睡很快醒后. 随即排便,试答大便化验, ...

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

    这是一些技巧,说明如何进行代码的逻辑正确性测试(与多线程正确性相对). 我发现本质上有两种带有线程代码的刻板印象模式: 面向任务–许多短期运行的同类任务,通常在Java 5执行程序框架内运行, 面向流 ...

  5. python停止线程池_详解python中Threadpool线程池任务终止示例代码

    需求 加入我们需要处理一串个位数(0~9),奇数时需要循环打印它:偶数则等待对应时长并完成所有任务:0则是错误,但不需要终止任务,可以自定义一些处理. 关键点 定义func函数处理需求 callbac ...

  6. python获取电脑几核几线程_python编程测试电脑开启最大线程数实例代码

    本文实例代码主要实现python编程测试电脑开启最大线程数,具体实现代码如下. #!/usr/bin/env python #coding=gbk import threading import ti ...

  7. 测试反模式冰激凌模式的不足_单元测试反模式,完整列表

    测试反模式冰激凌模式的不足 我前段时间写过有关OOP中的反模式的文章 . 现在该写单元测试反模式了,因为它们也存在,并且有很多. 我将尝试在列表中包括我知道的每个示例. 如果您认识其他任何人,请通过请 ...

  8. junit测试线程_一个在自己的线程中运行测试的JUnit规则

    junit测试线程 有时,能够在单独的线程中运行JUnit测试会很有帮助. 特别是在编写与封装的ThreadLocal或类似对象进行交互的集成测试时,这可能会派上用场. 单独的线程将隐式确保每次测试运 ...

  9. 单元测试 问题描述_单元测试技巧:创建描述性测试

    单元测试 问题描述 您的单元测试应尽可能具有描述性. 他们给您的反馈应该非常清楚,您甚至不必启动调试器,并一步一步地检查代码以检查局部变量. 为什么? 因为那需要时间,而且我们很懒,对吗? 为此,您需 ...

最新文章

  1. vsftpd+pam+mysql实现ftp构建
  2. Fedora 30将获得Bash 5.0,淘汰Yum推迟到Fedora 31
  3. pytorch切片,numpy切片的总结,以及数组切片常用操作的总结
  4. python counter_教你Python的collections.Counter类型
  5. 计算男孩女孩小孩各有几个 java——CSDN博客
  6. 位运算实现一些小算法
  7. php strchr 截断,PHP strchr() 函数
  8. 值得一生收藏的网站资源 没用过就太可惜了
  9. Android JNI学习(五)——Java与Native之间如何实现相互调用
  10. 【软件体系结构】考点整理
  11. 6.ring3-ImportREC重建输入表
  12. SpaceX的代码开源了,来看看火箭技术的代码.Spacex Rest API设计很有参考价值
  13. 传智博客JAVA基础第二十三天
  14. ESP8266 alios things 自带 linkkitapp OTA 更新失败
  15. 零基础想学大数据?你需要这个完整学习路线
  16. 读书虽苦,却是最容易的那条!
  17. 详述break、return、continue的区别
  18. 系统自带功能之视频压缩
  19. 建站过程中如何防止被骗
  20. 【c++】有理数加法

热门文章

  1. Java线程池,从使用到原理
  2. [初级]Java命令学习系列(六)——jinfo
  3. python打包exe文件
  4. idea如何安装lombok
  5. 用正则判断字符串是否为中文的方法
  6. 使用阿里云智能翻译接口案例——CSDN博客
  7. SpringMVC对Ajax请求的处理
  8. PHP WEB程序设计信息表,PHP WEB程序设计
  9. 计算机视觉论文doc,嘉炬-计算机视觉论文资料.doc
  10. MySQL的CRUD操作+使用视图