到目前为止,我已经避免了测试多线程代码的噩梦,因为它似乎是一个太大的雷区。我想知道人们是如何测试依赖于线程的代码以获得成功执行的,或者人们是如何测试那些仅在两个线程以给定方式交互时才会出现的问题的?
对于今天的程序员来说,这似乎是一个非常关键的问题,恕我直言,将我们的知识集中在这个问题上是很有用的。
到目前为止,我已经避免了测试多线程代码的噩梦,因为它似乎是一个太大的雷区。我想知道人们是如何测试依赖于线程的代码以获得成功执行的,或者人们是如何测试那些仅在两个线程以给定方式交互时才会出现的问题的?
对于今天的程序员来说,这似乎是一个非常关键的问题,恕我直言,将我们的知识集中在这个问题上是很有用的。
当前回答
听着,要做到这一点并不容易。我正在做一个本来就是多线程的项目。事件来自操作系统,我必须并发地处理它们。
处理测试复杂的多线程应用程序代码的最简单方法是:如果它太复杂而无法测试,那么您做错了。如果您有一个单独的实例,其中有多个线程作用于它,并且您无法测试这些线程相互踩在一起的情况,那么您的设计需要重做。它既简单又复杂。
有许多方法可以为多线程编程,以避免线程同时通过实例运行。最简单的方法是使所有对象都是不可变的。当然,这通常是不可能的。因此,您必须在设计中确定线程与同一实例交互的地方,并减少这些地方的数量。通过这样做,您可以隔离多线程实际发生的几个类,从而降低测试系统的总体复杂性。
但是您必须意识到,即使这样做,您仍然不能测试两个线程相互践踏的每一种情况。要做到这一点,您必须在同一个测试中并发地运行两个线程,然后准确地控制它们在任何给定时刻执行的行。你能做的就是模拟这种情况。但这可能需要您专门为测试编写代码,这充其量是迈向真正解决方案的半步。
测试代码是否存在线程问题的最好方法可能是对代码进行静态分析。如果您的线程代码没有遵循有限的线程安全模式集,那么您可能会遇到问题。我相信VS中的代码分析确实包含了一些线程的知识,但可能不多。
看,就目前的情况来看(可能还会持续很长一段时间),测试多线程应用程序的最佳方法是尽可能降低线程代码的复杂性。最小化线程交互的区域,尽可能地进行测试,并使用代码分析来识别危险区域。
其他回答
我曾经有过测试线程代码的不幸任务,这绝对是我写过的最难的测试。
在编写测试时,我使用委托和事件的组合。基本上,它都是关于使用PropertyNotifyChanged事件和WaitCallback或某种轮询的ConditionalWaiter。
我不确定这是否是最好的方法,但它对我来说是有效的。
我喜欢编写两个或多个测试方法在并行线程上执行,并且每个方法都调用被测对象。我一直在使用Sleep()调用来协调来自不同线程的调用顺序,但这并不真正可靠。它也慢得多,因为你必须睡足够长的时间,时间通常是有效的。
我从编写FindBugs的同一组中找到了多线程TC Java库。它允许您在不使用Sleep()的情况下指定事件的顺序,而且它是可靠的。我还没试过。
这种方法的最大限制是它只允许您测试您怀疑会引起麻烦的场景。正如其他人所说,您确实需要将多线程代码隔离到少量简单类中,以便有希望彻底测试它们。
一旦您仔细测试了您预计会导致问题的场景,那么在类中抛出一堆并发请求的不科学测试是寻找意外问题的好方法。
更新:我已经玩了一些多线程TC Java库,它工作得很好。我还将它的一些特性移植到一个。net版本,我称之为TickingTest。
(如果可能的话)不要使用线程,使用actor /活动对象。易于测试。
如果你正在测试简单的new Thread(runnable).run() 您可以模拟Thread来按顺序运行可运行对象
例如,如果被测试对象的代码像这样调用一个新线程
Class TestedClass {
public void doAsychOp() {
new Thread(new myRunnable()).start();
}
}
然后模拟new Threads并按顺序运行runable参数会有所帮助
@Mock
private Thread threadMock;
@Test
public void myTest() throws Exception {
PowerMockito.mockStatic(Thread.class);
//when new thread is created execute runnable immediately
PowerMockito.whenNew(Thread.class).withAnyArguments().then(new Answer<Thread>() {
@Override
public Thread answer(InvocationOnMock invocation) throws Throwable {
// immediately run the runnable
Runnable runnable = invocation.getArgumentAt(0, Runnable.class);
if(runnable != null) {
runnable.run();
}
return threadMock;//return a mock so Thread.start() will do nothing
}
});
TestedClass testcls = new TestedClass()
testcls.doAsychOp(); //will invoke myRunnable.run in current thread
//.... check expected
}
有一些很好的工具。下面是一些Java的摘要。
一些好的静态分析工具包括FindBugs(提供了一些有用的提示)、JLint、Java Pathfinder (JPF & JPF2)和Bogor。
multithreaddtc是一个非常好的动态分析工具(集成到JUnit中),您必须在其中设置自己的测试用例。
IBM研究院的竞赛很有趣。它通过插入各种线程修改行为(例如sleep & yield)来检测你的代码,试图随机发现错误。
SPIN是对Java(和其他)组件建模的一个非常酷的工具,但是您需要一些有用的框架。它很难使用,但如果你知道如何使用它,它是非常强大的。相当多的工具在底层使用SPIN。
multithreaddtc可能是最主流的,但是上面列出的一些静态分析工具绝对值得一看。