怎么做自己的发卡网站,ios软件开发需要学什么,深圳网站建设开发哪家好,网站怎么制作 推广这是一些技巧#xff0c;说明如何进行代码的逻辑正确性测试#xff08;与多线程正确性相对#xff09;。 我发现本质上有两种带有线程代码的刻板印象模式#xff1a; 面向任务–许多短期运行的同类任务#xff0c;通常在Java 5执行程序框架内运行#xff0c; 面向流程–… 这是一些技巧说明如何进行代码的逻辑正确性测试与多线程正确性相对。 我发现本质上有两种带有线程代码的刻板印象模式 面向任务–许多短期运行的同类任务通常在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 testBeforepublic 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 - dont 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 ListRunnable 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 eventBeforepublic 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