
本文详细探讨了在java单元测试中,如何正确模拟异常适配器以覆盖try-catch块中的异常处理逻辑。核心在于理解mockito中thenreturn()和thenthrow()的区别,当被测试方法通过适配器返回一个异常对象并自行抛出时,应使用thenreturn()来模拟适配器的行为,而非让适配器直接thenthrow(),从而确保测试的准确性和覆盖率。
在软件开发中,异常处理是确保系统健壮性的关键环节。对包含异常处理逻辑的代码进行单元测试,特别是当异常通过适配器进行转换时,需要精确地模拟外部依赖的行为。本文将深入探讨如何正确地单元测试Java中try-catch块内涉及异常适配器的代码,以确保异常路径得到充分覆盖。
考虑以下Java方法,它从一个客户端获取信息,并在遇到特定客户端或内部服务器错误时,通过一个异常适配器将其转换为服务层异常并重新抛出:
public Method execute(@NonNull final String test) throws ServiceException {
Object object; // 假设Method类型可以存储这个object
try {
object = javaClient.fetchInfo(test);
} catch (ClientException | InternalServerError e) {
// 关键点:serviceExceptionAdapter.apply(e) 返回一个ServiceException实例
throw serviceExceptionAdapter.apply(e);
}
return object;
}在这个execute方法中,javaClient.fetchInfo(test)可能会抛出ClientException或InternalServerError。当这些异常被捕获时,serviceExceptionAdapter.apply(e)会被调用。根据代码结构,apply方法显然是接收一个异常,并返回一个ServiceException的实例,然后execute方法再将这个返回的ServiceException实例抛出。
为了测试上述catch块中的异常路径,特别是InternalServerError的情况,我们可能会编写如下的单元测试:
class ProxyTest {
private ExceptionAdapter serviceExceptionAdapter;
private JavaClient mockJavaClient;
private Proxy proxy; // 假设Proxy是包含execute方法的类
@BeforeEach
void setup() {
this.serviceExceptionAdapter = mock(ExceptionAdapter.class);
this.mockJavaClient = mock(JavaClient.class);
proxy = new Proxy(mockJavaClient, serviceExceptionAdapter);
}
@Test
void test_InternalServerError() {
String testInput = "someTestValue"; // 定义一个测试输入
// 模拟javaClient抛出InternalServerError
when(mockJavaClient.fetchInfo(any())).thenThrow(InternalServerError.class);
// 尝试模拟异常适配器抛出ServiceException
when(serviceExceptionAdapter.apply(any())).thenThrow(ServiceException.class);
// 断言execute方法会抛出ServiceException
assertThrows(ServiceException.class, () -> proxy.execute(testInput));
// 验证适配器被调用了一次
verify(serviceExceptionAdapter, times(1)).apply(any());
}
}尽管上述测试断言了ServiceException的抛出并验证了适配器的调用,但它可能无法正确覆盖catch块中的所有逻辑,甚至可能导致测试失败或误导。核心问题出在这一行:
when(serviceExceptionAdapter.apply(any())).thenThrow(ServiceException.class);
这里我们试图让serviceExceptionAdapter.apply(any())方法直接抛出一个ServiceException。然而,回顾被测方法:
throw serviceExceptionAdapter.apply(e);
execute方法期望serviceExceptionAdapter.apply(e)返回一个ServiceException实例,然后由execute方法自身通过throw关键字将其抛出。如果适配器方法被模拟为直接thenThrow(),那么execute方法在调用apply时就会捕获到这个由Mockito模拟的异常,而不是按照其内部逻辑接收一个返回的异常对象并抛出。这与实际生产代码的行为不符。
正确的做法是模拟serviceExceptionAdapter.apply(e)方法返回一个ServiceException实例,而不是让它直接抛出。execute方法会接收到这个返回的异常实例,并按照其设计将其抛出。
修改后的测试代码如下:
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.assertThrows;
// 假设这些是您的异常和适配器接口
interface JavaClient {
Object fetchInfo(String test) throws ClientException, InternalServerError;
}
interface ExceptionAdapter {
ServiceException apply(Exception e);
}
class ServiceException extends Exception {
public ServiceException(String message) { super(message); }
public ServiceException(String message, Throwable cause) { super(message, cause); }
}
class ClientException extends Exception {}
class InternalServerError extends Exception {}
// Proxy类,包含要测试的execute方法
class Proxy {
private final JavaClient javaClient;
private final ExceptionAdapter serviceExceptionAdapter;
public Proxy(JavaClient javaClient, ExceptionAdapter serviceExceptionAdapter) {
this.javaClient = javaClient;
this.serviceExceptionAdapter = serviceExceptionAdapter;
}
public Object execute(@NonNull final String test) throws ServiceException {
Object object;
try {
object = javaClient.fetchInfo(test);
} catch (ClientException | InternalServerError e) {
throw serviceExceptionAdapter.apply(e);
}
return object;
}
}
class ProxyTest {
private ExceptionAdapter serviceExceptionAdapter;
private JavaClient mockJavaClient;
private Proxy proxy;
@BeforeEach
void setup() {
this.serviceExceptionAdapter = mock(ExceptionAdapter.class);
this.mockJavaClient = mock(JavaClient.class);
proxy = new Proxy(mockJavaClient, serviceExceptionAdapter);
}
@Test
void test_InternalServerError_withCorrectAdapterMocking() {
String testInput = "someTestValue";
// 1. 模拟javaClient抛出InternalServerError,触发catch块
when(mockJavaClient.fetchInfo(any())).thenThrow(InternalServerError.class);
// 2. 模拟serviceExceptionAdapter.apply(any()) 返回一个ServiceException实例
// 这一步至关重要,它模拟了适配器“创建”并“返回”一个异常对象
when(serviceExceptionAdapter.apply(any()))
.thenReturn(new ServiceException("Mocked Service Exception from Adapter"));
// 3. 断言proxy.execute()会抛出ServiceException
assertThrows(ServiceException.class, () -> proxy.execute(testInput));
// 4. 验证serviceExceptionAdapter.apply()方法被调用了一次
verify(serviceExceptionAdapter, times(1)).apply(any());
}
}通过将thenThrow(ServiceException.class)改为thenReturn(new ServiceException(...)),我们准确地模拟了serviceExceptionAdapter.apply(e)的预期行为——返回一个ServiceException实例。这样,execute方法就能接收到这个实例并按照其内部逻辑将其抛出,从而确保了对catch块的正确单元测试覆盖。
通过以上方法,我们可以确保单元测试准确地反映代码的实际行为,从而提高测试的质量和可靠性。
以上就是如何正确单元测试捕获异常语句中的异常适配器的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号