怎么在360搜索做网站推广,网站基础维护,合肥工程建设网站,网页qq登录入口首页错误和异常处理是测试中非常重要的部分。假设我们有一个服务#xff0c;该服务从数据库中获取用户。现在#xff0c;我们要考虑的错误场景是#xff1a;数据库连接断开。 整体代码示例
首先#xff0c;为了简化#xff0c;我们让服务层就是简单的类#xff0c;然后使用I… 错误和异常处理是测试中非常重要的部分。假设我们有一个服务该服务从数据库中获取用户。现在我们要考虑的错误场景是数据库连接断开。 整体代码示例
首先为了简化我们让服务层就是简单的类然后使用Id查找用户这个和之前测试UserService接口不太一样哦
Service
public class UserService {Autowiredprivate UserRepository userRepository;public User getUserById(Long id) {return userRepository.findById(id).orElse(null);}
}现在我们要模拟UserRepository的行为使其在尝试获取用户时引发一个异常。这里我们使用Mockito进行模拟
RunWith(SpringRunner.class)
SpringBootTest
public class UserServiceTest {//之前我们是定义了一个UserService接口现在简化成UserService类了哈InjectMocksprivate UserService userService;Mockprivate UserRepository userRepository;Beforepublic void setUp() throws Exception {MockitoAnnotations.initMocks(this);}//重点后文详解Test(expected DatabaseConnectionException.class)public void testGetUserByIdWithDbError() {when(userRepository.findById(anyLong())).thenThrow(new DatabaseConnectionException(Database connection failed!));userService.getUserById(1L);}
}//重点后文详解
class DatabaseConnectionException extends RuntimeException {public DatabaseConnectionException(String message) {super(message);}
}在上述测试中我们模拟了userRepository.findById()方法使其抛出DatabaseConnectionException异常。然后我们在测试方法上使用Test(expected DatabaseConnectionException.class)来表示我们期望该方法引发此异常。
这样如果getUserById方法在遇到此异常时没有正确处理测试将失败。这确保了即使在面对意外的数据库问题时我们的代码仍能按预期的方式运行在这种情况下按预期抛出异常。 到底在模拟什么到底在测试什么
下面我们进一步说明 测试目标这个测试的目标是确保当userRepository.findById()方法抛出DatabaseConnectionException异常时userService.getUserById()方法也会抛出同样的异常。 模拟异常在这行代码中我们指定了当userRepository.findById()被调用时它应该抛出DatabaseConnectionException异常。 when(userRepository.findById(anyLong())).thenThrow(new DatabaseConnectionException(Database connection failed!));调用Service方法接下来我们调用了userService.getUserById(1L)。我们期望它在内部调用userRepository.findById()这在实际的UserService实现中应该是这样的。因此由于我们已经模拟了userRepository.findById()来抛出异常所以userService.getUserById()也应该会抛出这个异常。 验证异常Test(expected DatabaseConnectionException.class)注解表示我们期望这个测试方法在执行时会抛出DatabaseConnectionException异常。如果这个方法执行完并没有抛出这个异常那么测试将会失败。 测试的目的这个测试的目的并不是检查userRepository.findById()本身是否真的会抛出异常而是检查当它抛出异常时userService.getUserById()是否会正确地传递这个异常。这可以帮助我们确保UserService在处理异常时的行为是正确的。其实本质上来说抛出异常和预期值的测试逻辑几乎是一样的都是通过给定下层值验证上层代码关系。 综上所述这个测试确保了当底层UserRepository出现数据库连接错误时上层的UserService可以正确地传递这个错误。这对于后续的异常处理很重要例如在Controller层将这个异常转化为一个友好的错误消息返回给用户。 什么时候测试失败 在正常情况下只要Service层确实调用了Repository的方法并且Repository的方法抛出了RuntimeException或其子类那么Service层的调用方法也应该会收到并进一步抛出这个异常。 但是以下几种情况可能导致测试不通过 异常被吞没如果Service层调用了Repository的方法但内部捕获了该异常并没有重新抛出那么测试就会失败。例如 public User getUserById(Long id) {try {return userRepository.findById(id);} catch (DatabaseConnectionException e) {// 异常被吞没了return null;}
}调用的方法不正确如果Service层没有调用预期的Repository方法而是调用了其他方法或者完全没有调用那么模拟的异常就不会被触发导致测试失败。 模拟的不正确如果在测试中模拟的方法或参数与实际调用的方法或参数不匹配那么模拟的异常也不会被触发。例如如果Service实际上是这样调用的userRepository.findById(2L)但我们的模拟是这样的when(userRepository.findById(1L))...那么异常就不会被触发。 其他未预料到的异常有时可能会有其他的未被预料到的异常被抛出这也会导致测试失败。
因此虽然大多数情况下如果Repository层方法抛出了异常Service层应该也会抛出但还是存在一些情况导致测试不通过这也是进行此类测试的原因。 Exception 异常类定义
class DatabaseConnectionException extends RuntimeException {public DatabaseConnectionException(String message) {super(message);}
} DatabaseConnectionException是一个自定义的异常类。在Java中异常是用来表示程序运行中的问题或异常情况的对象。当某些问题发生时通常会抛出throw一个异常。 这里我们定义了一个继承自RuntimeException的新异常类DatabaseConnectionException。RuntimeException是Java中所有非检查型异常的基类。所谓“非检查型”是指编译器不强制我们捕获或声明它。这与Exception检查型异常相对。
关于DatabaseConnectionException类的解释 class DatabaseConnectionException extends RuntimeException - 这表示我们正在定义一个名为DatabaseConnectionException的新类该类是RuntimeException的子类。这意味着DatabaseConnectionException继承了RuntimeException的所有特性。 public DatabaseConnectionException(String message) - 这是DatabaseConnectionException类的构造方法。当我们创建DatabaseConnectionException的新实例时可以传递一个消息字符串给这个构造函数。 super(message); - 这行代码调用了父类RuntimeException的构造方法并将message传递给它。这样当异常被抛出并捕获时我们可以获取并显示这个消息。 这种自定义异常通常在我们希望为特定的错误情况定义更具描述性的异常名时使用或者当我们想为特定的异常情况添加更多上下文信息时使用信息越多测试反馈的效果越好所以一般使用自定义异常继承RuntimeException下面我们讨论一下为什么建议使用RuntimeException RuntimeException 使用意义 使用RuntimeException非检查型异常还是Exception检查型异常来自定义数据库异常或其他异常是一个设计决策并且这两者在Java中有不同的含义和用途。 下面是一些选择使用RuntimeException的原因 不需要显式处理当方法中抛出非检查型异常时调用该方法的代码不需要显式地处理异常即不需要使用try-catch或在方法签名中使用throws。这使得代码更简洁更易读。 表示编程错误非检查型异常通常用于表示编程错误例如空指针异常、数组越界等。对于某些数据库异常如配置错误这可能是一个编程错误因此使用RuntimeException可能更合适。 强制开发者考虑异常处理策略使用检查型异常会强制调用者处理异常这可能会导致过多的try-catch块并使代码复杂化。而使用非检查型异常开发者可以选择在何处处理异常这通常会导致更好、更集中的异常处理策略。 与现有框架兼容许多现代Java框架如Spring倾向于使用非检查型异常因为它们认为异常应该在应用程序的高层如Controller或Service中统一处理。 灵活性有时在开发过程的后期可能会发现某些异常不再是关键的不需要强制处理。对于非检查型异常这意味着不需要修改方法签名或调用代码。 然而这并不意味着总是应该选择非检查型异常。有时如果你希望调用者必须处理某个特定的异常使用检查型异常可能更合适。选择使用哪种异常是基于特定上下文和需求的决策。但在许多现代Java应用程序中倾向于使用RuntimeException因为它提供了更大的灵活性和简洁性。 总结
模拟异常的目的 验证代码在遇到异常时是否有正确的响应例如是否抛出了预期的异常。确保代码在异常情况下仍然能够维持预期的状态或行为。单元测试通常关注隔离性因此模拟异常可以确保在不涉及实际外部依赖的情况下模拟各种可能的场景。 真正的数据库异常是不是Runtime异常
在Java中数据库操作可能会抛出多种异常。其中SQLException 是一个受检异常checked exception。
但在很多现代的框架中如Spring这些受检异常通常会被转换成运行时异常runtime exceptions这样可以使代码更为简洁避免了过多的try-catch块。