gpt4 book ai didi

java - 对线程应用程序进行单元测试

转载 作者:行者123 更新时间:2023-12-01 19:50:19 25 4
gpt4 key购买 nike

我正在思考如何使用mockito为此编写测试用例。

例如,我的主线程中的部分逻辑是创建一个执行 3 件事的线程。请参阅下面我带注释的代码。

现在,根据来自主程序的输入数量,RequestThread 可以多次生成。

public class MainThreads {
public static void main(String[] args) {
RequestThread rt = new RequestThread("sample");
rt.start();

//RequestThread another = new RequestThread("sample-2");
//another.start();

//RequestThread newThread = new RequestThread("sample-3");
//newThread.start();
}

public static class RequestThread implements Runnable{
private final String request;

public RequestThread(String request) {
this.request = request;
}

@Override
public void run() {
//1. Instantiate a service passing the required request parameter
MyDataWebService service = new MyDataWebService(request);

//2. Get the returned data
List<String> dataList = service.requestData();

//3. Write to file
Path file = Paths.get("/someDir/" + request);
Files.write(file, dataList, Charset.forName("UTF-8"));
}

}
}

我的问题是,我无法弄清楚如何为线程类正确编写 JUnit/Mockito 测试。一般来说,我不太熟悉 Mockito 和 JUnit,所以我正在寻找一种进行单元测试的方法线程应用程序。

有人可以指导我如何对这样的东西进行单元测试吗?

最佳答案

您需要对代码进行一些更改,以使其更易于测试。特别是:

  • 您想要模拟的对象应该实现一个接口(interface)
  • 不要在要测试的函数中实例化要模拟的对象

这里重写了这些类,以便您可以模拟 MyDataWebService 并测试 RequestThread。基于此示例,您将能够更轻松地为 MainThreads 类编写完整的测试。

public class MainThreads {
public static void main(String[] args) {
RequestThread rt = new RequestThread("sample");
rt.start();

//RequestThread another = new RequestThread("sample-2");
//another.start();

//RequestThread newThread = new RequestThread("sample-3");
//newThread.start();
}

public static class RequestThread extends Thread {
private final String request;
// One important thing to note here, "service" has to be non-final. Else mockito won't be able to inject the mock.
private MyDataWebServiceInterface service;

public RequestThread(String request) {
this.request = request;
//1. Instantiate a service passing the required request parameter
// => do it in constructor, or passed as parameter, but NOT in the function to test
service = new MyDataWebService(request);
}

@Override
public void run() {
//2. Get the returned data
List<String> dataList = service.requestData();

//3. Write to file
Path file = Paths.get("someDir/" + request);
try {
Files.write(file, dataList, Charset.forName("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

MyDataWebService 的接口(interface)和实现:

interface MyDataWebServiceInterface {
List<String> requestData();
}

class MyDataWebService implements MyDataWebServiceInterface {
public MyDataWebService(String request) {
}

@Override
public List<String> requestData() {
return Arrays.asList("foo", "bar");
}
}

并使用mockito进行测试。请注意,检查现有文件和线程 sleep 可能不是这里最优雅的事情。如果您可以在 RequestThread 中添加一些标记来指示数据已写入,那么它肯定会使测试更好、更安全(文件系统 I/O 有时很难测试)。

@RunWith(MockitoJUnitRunner.class)
public class RequestThreadTest {

private static final Path FILE = Paths.get("someDir", "sample");

@Mock
MyDataWebServiceInterface service;

@InjectMocks
MainThreads.RequestThread reqThread = new MainThreads.RequestThread("sample");

@Before
public void setup() throws IOException, InterruptedException {
if (Files.exists(FILE)) {
Files.delete(FILE);
while (Files.exists(FILE)) {
Thread.sleep(50);
}
}
}

@Test
public void shouldWriteFile() throws InterruptedException {
Mockito.when(service.requestData()).thenReturn(Arrays.asList("one", "two"));
reqThread.start();
while (!Files.exists(FILE)) {
Thread.sleep(50);
}
// HERE run assertions about file content
}
}

现在,测试异步代码通常比同步代码更复杂,因为您经常会面临非确定性行为、计时问题等。您可能想在测试中设置超时,但请记住:持续集成工具(jenkins、travis等)通常会比您的机器运行得慢,这是问题的常见原因,因此不要将其设置得太紧。据我所知,对于非确定性问题,没有“一刀切”的解决方案。

Martin Fowler 有一篇关于测试中的非确定性的优秀文章:https://martinfowler.com/articles/nonDeterminism.html

关于java - 对线程应用程序进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51510823/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com