我有一段Java代码,它在一个主线程中运行。从主线程中,我创建了一个新的线程,在新线程中进行了一次服务器调用。等待服务器响应后,我在新线程中做了一些工作,然后代码再次回到主线程。
我使用Eclipse Jobs来执行服务器调用。
我想知道如何为这个代码编写一个JUnit测试用例。
我有一段Java代码,它在一个主线程中运行。从主线程中,我创建了一个新的线程,在新线程中进行了一次服务器调用。等待服务器响应后,我在新线程中做了一些工作,然后代码再次回到主线程。
我使用Eclipse Jobs来执行服务器调用。
我想知道如何为这个代码编写一个JUnit测试用例。
你可能需要重构你的代码,使其更易于测试。
我可以看到几个明显的测试领域:
结构化你的实现,使你的线程管理代码对工作者的细节无所谓。然后,你可以使用模拟工作者来测试线程管理 - 例如,一个在某些方面失败的模拟工作者允许你测试管理代码中的某些路径。
实现工作者代码使其能够独立运行。然后,你可以使用服务器的模拟来单独对其进行单元测试。
对于并发测试,Abhijeet Kashnia提供的链接将有所帮助。
当你唯一的问题是等待结果时,使用 ExecutorService 来生成线程。它可以接受工作任务作为 Runnable 和 Callable。当你使用后者时,你将得到一个 Future 对象作为返回值,用于等待结果。无论如何,您都应该考虑使用 ExecutorService,因为从我的理解来看,您创建了许多线程,而这正是执行器服务的完美用例。
class AnyClass {
private ExecutorService threadPool = Executors.newFixedThreadPool(5);
public List<Future<Integer>> anyMethod() {
List<Future> futures = new ArrayList<>();
futures.add(threadPool.submit(() -> {
// Do your job here
return anyStatusCode;
}));
futures.add(threadPool.submit(() -> {
// Do your other job here
return anyStatusCode;
}));
return futures;
}
}
还有测试类:
class TestAnyClass {
@Test
public void testAnyMethod() {
AnyClass anyObject = new AnyClass();
List<Future<Integer>> futures = anyObject.anyMethod();
CompletableFuture[] completable = futures.toArray(new CompletableFuture[futures.size()]);
// Wait for all
CompletableFuture.allOf(completable).join();
}
}
我猜您可能已经完成了模拟代码,并希望进行简单的集成测试,以确保您的服务器调用起作用。
测试线程存在的一个困难在于它们的本质-它们是并发的。这意味着您被迫编写JUnit测试代码,强制等待您的线程完成其工作,然后再测试您的代码结果。这不是一种很好的测试代码的方式,可能会不可靠,但通常可以让您了解您的代码是否有效。
例如,您的代码可能如下所示:
@Test
public void myIntegrationTest() throws Exception {
// Setup your test
// call your threading code
Results result = myServerClient.doThreadedCode();
// Wait for your code to complete
sleep(5);
// Test the results
assertEquals("some value",result.getSomeValue());
}
private void sleep(int seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
我真的不喜欢这样做,更喜欢模拟,并且同意其他答案。但是,如果您需要测试您的线程,则这是我发现有效的一种方法。
这是我对使用 thread.start 的异步方法进行测试的解决方案:
public class MyClass {
public void doSomthingAsynchrone() {
new Thread(() -> {
doSomthing();
}).start();
}
private void doSomthing() {
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest(MyClass.class)
public class MyClassTest {
ArgumentCaptor<Runnable> runnables = ArgumentCaptor.forClass(Runnable.class);
@InjectMocks
private MyClass myClass;
@Test
public void shouldDoSomthingAsynchrone() throws Exception {
// create a mock for Thread.class
Thread mock = Mockito.mock(Thread.class);
// mock the 'new Thread', return the mock and capture the given runnable
whenNew(Thread.class).withParameterTypes(Runnable.class)
.withArguments(runnables.capture()).thenReturn(mock);
myClass.doSomthingAsynchrone();
runnables.getValue().run();
/**
* instead of 'runnables.getValue().run();' you can use a real thread.start
*
* MockRepository.remove(Thread.class);
* Thread thread = new Thread(runnables.getValue());
* thread.start();
* thread.join();
**/
verify(myClass, times(1)).doSomthing();
}
}