当前位置: 代码网 > it编程>软件设计>软件测试 > Junit 单元测试之错误和异常处理

Junit 单元测试之错误和异常处理

2024年08月06日 软件测试 我要评论
5. 测试的目的:这个测试的目的并不是检查userRepository.findById()本身是否真的会抛出异常,而是检查当它抛出异常时,userService.getUserById()是否会正确地传递这个异常。这种自定义异常,通常在我们希望为特定的错误情况定义更具描述性的异常名时使用,或者当我们想为特定的异常情况添加更多上下文信息时使用,信息越多,测试反馈的效果越好,所以一般使用自定义异常,继承RuntimeException!有时,如果你希望调用者必须处理某个特定的异常,使用检查型异常可能更合适。

整体代码示例

首先,为了简化,我们让服务层就是简单的类,然后使用id查找用户,这个和之前测试userservice接口不太一样哦:​​​​​​

  1.  @service

  2.   public class userservice {

  3.   

  4.    @autowired

  5.    private userrepository userrepository;

  6.   

  7.    public user getuserbyid(long id) {

  8.    return userrepository.findbyid(id).orelse(null);

  9.    }

  10.   }

现在,我们要模拟userrepository的行为,使其在尝试获取用户时引发一个异常。这里我们使用mockito进行模拟:​​​​​

 
  1.  @runwith(springrunner.class)

  2.   @springboottest

  3.   public class userservicetest {

  4.   

  5.   //之前我们是定义了一个userservice接口,现在简化成userservice类了哈

  6.    @injectmocks

  7.    private userservice userservice;

  8.   

  9.    @mock

  10.    private userrepository userrepository;

  11.   

  12.    @before

  13.    public void setup() throws exception {

  14.    mockitoannotations.initmocks(this);

  15.    }

  16.   

  17.   //重点,后文详解!

  18.    @test(expected = databaseconnectionexception.class)

  19.    public void testgetuserbyidwithdberror() {

  20.    when(userrepository.findbyid(anylong())).thenthrow(new databaseconnectionexception("database connection failed!"));

  21.   

  22.    userservice.getuserbyid(1l);

  23.    }

  24.   }

  25.   

  26.   //重点,后文详解!

  27.   class databaseconnectionexception extends runtimeexception {

  28.    public databaseconnectionexception(string message) {

  29.    super(message);

  30.    }

  31.   }

在上述测试中,我们模拟了userrepository.findbyid()方法,使其抛出databaseconnectionexception异常。然后,我们在测试方法上使用@test(expected = databaseconnectionexception.class)来表示我们期望该方法引发此异常。

这样,如果getuserbyid方法在遇到此异常时没有正确处理,测试将失败。这确保了即使在面对意外的数据库问题时,我们的代码仍能按预期的方式运行(在这种情况下,按预期抛出异常)。

到底在模拟什么?到底在测试什么?

下面,我们进一步说明:

1. 测试目标:这个测试的目标是确保当userrepository.findbyid()方法抛出databaseconnectionexception异常时,userservice.getuserbyid()方法也会抛出同样的异常。

2. 模拟异常:在这行代码中,我们指定了当userrepository.findbyid()被调用时,它应该抛出databaseconnectionexception异常。

when(userrepository.findbyid(anylong())).thenthrow(new databaseconnectionexception("database connection failed!"));

3. 调用service方法:接下来,我们调用了userservice.getuserbyid(1l)。我们期望它在内部调用userrepository.findbyid()(这在实际的userservice实现中应该是这样的)。因此,由于我们已经模拟了userrepository.findbyid()来抛出异常,所以userservice.getuserbyid()也应该会抛出这个异常。

4. 验证异常:@test(expected = databaseconnectionexception.class)注解表示我们期望这个测试方法在执行时会抛出databaseconnectionexception异常。如果这个方法执行完并没有抛出这个异常,那么测试将会失败。

5. 测试的目的:这个测试的目的并不是检查userrepository.findbyid()本身是否真的会抛出异常,而是检查当它抛出异常时,userservice.getuserbyid()是否会正确地传递这个异常。这可以帮助我们确保userservice在处理异常时的行为是正确的。其实本质上来说,抛出异常和预期值的测试逻辑几乎是一样的,都是通过给定下层值,验证上层代码关系。

综上所述,这个测试确保了当底层userrepository出现数据库连接错误时,上层的userservice可以正确地传递这个错误。这对于后续的异常处理很重要,例如:在controller层将这个异常转化为一个友好的错误消息返回给用户。

什么时候测试失败?

在正常情况下,只要service层确实调用了repository的方法,并且repository的方法抛出了runtimeexception(或其子类),那么service层的调用方法也应该会收到并进一步抛出这个异常。

但是,以下几种情况可能导致测试不通过:

异常被吞没:如果service层调用了repository的方法,但内部捕获了该异常并没有重新抛出,那么测试就会失败。例如:​​​​

 
  1.  public user getuserbyid(long id) {

  2.    try {

  3.    return userrepository.findbyid(id);

  4.    } catch (databaseconnectionexception e) {

  5.    // 异常被吞没了

  6.    return null;

  7.    }

  8.   }

调用的方法不正确:如果service层没有调用预期的repository方法,而是调用了其他方法,或者完全没有调用,那么模拟的异常就不会被触发,导致测试失败。

模拟的不正确:如果在测试中模拟的方法或参数与实际调用的方法或参数不匹配,那么模拟的异常也不会被触发。例如,如果service实际上是这样调用的:userrepository.findbyid(2l),但我们的模拟是这样的:when(userrepository.findbyid(1l))...,那么异常就不会被触发。

其他未预料到的异常:有时可能会有其他的未被预料到的异常被抛出,这也会导致测试失败。

因此,虽然大多数情况下,如果repository层方法抛出了异常,service层应该也会抛出,但还是存在一些情况导致测试不通过,这也是进行此类测试的原因。

exception 异常类定义​​​

 
  1.  class databaseconnectionexception extends runtimeexception {

  2.    public databaseconnectionexception(string message) {

  3.    super(message);

  4.    }

  5.   }

databaseconnectionexception是一个自定义的异常类。在 java中,异常是用来表示程序运行中的问题或异常情况的对象。当某些问题发生时,通常会抛出(throw)一个异常。

这里,我们定义了一个继承自runtimeexception的新异常类databaseconnectionexception。runtimeexception是java中所有非检查型异常的基类。所谓“非检查型”是指编译器不强制我们捕获或声明它。这与exception(检查型异常)相对。

关于databaseconnectionexception类的解释:

1. class databaseconnectionexception extends runtimeexception - 这表示我们正在定义一个名为databaseconnectionexception的新类,该类是runtimeexception的子类。这意味着databaseconnectionexception继承了runtimeexception的所有特性。

2. public databaseconnectionexception(string message) - 这是databaseconnectionexception类的构造方法。当我们创建databaseconnectionexception的新实例时,可以传递一个消息字符串给这个构造函数。

3. super(message); - 这行代码调用了父类(runtimeexception)的构造方法,并将message传递给它。这样,当异常被抛出并捕获时,我们可以获取并显示这个消息。

这种自定义异常,通常在我们希望为特定的错误情况定义更具描述性的异常名时使用,或者当我们想为特定的异常情况添加更多上下文信息时使用,信息越多,测试反馈的效果越好,所以一般使用自定义异常,继承runtimeexception!下面我们讨论一下,为什么建议使用runtimeexception?

runtimeexception 使用意义

使用runtimeexception(非检查型异常)还是exception(检查型异常)来自定义数据库异常(或其他异常)是一个设计决策,并且这两者在java中有不同的含义和用途。

下面是一些选择使用runtimeexception的原因:

1. 不需要显式处理:当方法中抛出非检查型异常时,调用该方法的代码不需要显式地处理异常(即不需要使用try-catch或在方法签名中使用throws)。这使得代码更简洁,更易读。

2. 表示编程错误:非检查型异常通常用于表示编程错误,例如空指针异常、数组越界等。对于某些数据库异常,如配置错误,这可能是一个编程错误,因此使用runtimeexception可能更合适。

3. 强制开发者考虑异常处理策略:使用检查型异常会强制调用者处理异常,这可能会导致过多的try-catch块并使代码复杂化。而使用非检查型异常,开发者可以选择在何处处理异常,这通常会导致更好、更集中的异常处理策略。

4. 与现有框架兼容:许多现代java框架,如spring,倾向于使用非检查型异常,因为它们认为异常应该在应用程序的高层(如controller或service)中统一处理。

5. 灵活性:有时,在开发过程的后期,可能会发现某些异常不再是关键的,不需要强制处理。对于非检查型异常,这意味着不需要修改方法签名或调用代码。

然而,这并不意味着总是应该选择非检查型异常。有时,如果你希望调用者必须处理某个特定的异常,使用检查型异常可能更合适。选择使用哪种异常是基于特定上下文和需求的决策。但在许多现代java应用程序中,倾向于使用runtimeexception因为它提供了更大的灵活性和简洁性。

总结

模拟异常的目的

  ·验证代码在遇到异常时是否有正确的响应,例如是否抛出了预期的异常。

  · 确保代码在异常情况下仍然能够维持预期的状态或行为。

  · 单元测试通常关注隔离性,因此模拟异常可以确保在不涉及实际外部依赖的情况下,模拟各种可能的场景。

真正的数据库异常是不是runtime异常

在java中,数据库操作可能会抛出多种异常。其中,sqlexception 是一个受检异常(checked exception)。

但在很多现代的框架中(如spring),这些受检异常通常会被转换成运行时异常(runtime exceptions),这样可以使代码更为简洁,避免了过多的try-catch块。

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取   

 

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com