V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
AS4694lAS4808
V2EX  ›  Java

单元测试的疑惑

  •  
  •   AS4694lAS4808 · 2022-06-29 09:53:43 +08:00 via Android · 2695 次点击
    这是一个创建于 638 天前的主题,其中的信息可能已经有所发展或是发生改变。
    最近写一个新项目,其中有一个函数是根据父级 id 从数据库查出几十条数据,发到 pulsar 等下游服务返回结果,再更新回数据库,逻辑很简单,也就不到 50 行代码。



    对于这块的单元测试不知道该怎么写。。因为有其他项目也在操作开发数据库,根据父级 id 可能查出来为空,这时逻辑是 ok 的。然后下游的服务也不一定一直监听消息,另外如果监听了,下游服务会产生业务逻辑更新它们的库。。所以这种情况该怎么写单元测试呢?



    如果测试方法只是调用函数,检查有没有异常,好像作用不大。如果是 sql 查出有结果的父级 id 再调用函数,感觉增加了测试的变量,jenkins 执行 mvn 的时候没准会失败。而且即使成功查出数据,还会造成下游服务的数据非正常更改。。



    之前单元测试写的少,都是比较好拆分的模块,最近要求测试模块详细完整了,所以有点摸不着头脑。。
    12 条回复    2022-06-29 23:37:02 +08:00
    zhazi
        1
    zhazi  
       2022-06-29 10:12:46 +08:00
    测试是测试你的 [代码逻辑] 。跟数据库完全隔离的
    var obj = new SomeObj() ;
    when(mockDb.findByParentId(any())).thenReturn(obj);
    business.logic(something);
    Mockito.verify(rpc.call(),times(1));

    doThing().when(mockDb.findByParentId(any());
    business.logic(something);
    Mockito.verify(rpc.call(),nvner());

    这种测试可以保证你的 business.logic 的逻辑

    至于你的 dao 层需要去断言 sql 写的是否正确 这种测试除非把业务写进 sql 里才需要去测试
    var query = JpaQueryFactory.select(*).from(tableA).where(tableA.name.equals("actualField");
    String expected = "select * from tableA where tableA.name = "actualField";
    Assertions.assertEquals(expected,query.toString());
    yinhuochong6666
        2
    yinhuochong6666  
       2022-06-29 10:12:52 +08:00
    既然他叫单元测试,还使用真实的上下游服务器数据库,这本身就是个错误
    timethinker
        3
    timethinker  
       2022-06-29 10:15:13 +08:00
    这个不是单元测试吧,这个算是集成测试了,需要在独立的测试进程中对其进行测试,就好像是应用程序的真实使用者一样。

    对于单元测试,如果测试的目标依赖了一个外部的组件,常用的做法是对这种外部组件进行 Mock ,比如测试目标需要一个发送消息的服务( MessagePublisher ),可以 Mock 一个消息发送服务( InMemoryMessagePublisher ),然后对测试目标进行测试,并观察 InMemoryMessagePublisher 中的结果是否符合预期,当然这只是一个很简单的例子,因为单元测试足够小,所以可以反复快速的完成测试内容。

    测试是一个很复杂的概念,每个人或者组织都有不同的偏好,在我看来归根结底无非就是两个问题,那就是搞清楚我们到底在测试什么?我们期望测试将来可以起到什么帮助?
    dayeye2006199
        4
    dayeye2006199  
       2022-06-29 10:30:04 +08:00 via Android
    Don't test what you do not own.

    不测试你不拥有的系统
    AS4694lAS4808
        5
    AS4694lAS4808  
    OP
       2022-06-29 10:32:59 +08:00
    @zhazi sql 基本都是 mybatis-plus 搞的,倒是没有验证 sql 语句本身的需求。

    好像有点明白了,就是我应该把函数内部的 dao 和 mq 操作的 service bean 放到类属性,用 mockito 来生成,然后再调用方法测试吧?(现在 bean 是函数内从 ApplicationContext 获取的。。)
    之前主要是想非侵入的写个测试方法,看来钻牛角尖了。。

    非常感谢!
    Morii
        6
    Morii  
       2022-06-29 10:33:18 +08:00   ❤️ 1
    你这里属于集成测试了,需要 mock 依赖的的服务、中间件,然后测试流程中插桩处理异常情况
    AS4694lAS4808
        7
    AS4694lAS4808  
    OP
       2022-06-29 10:33:59 +08:00
    @yinhuochong6666 很有道理,之前没想明白怎么 mock ,现在想明白了。。谢谢!
    AS4694lAS4808
        8
    AS4694lAS4808  
    OP
       2022-06-29 10:35:44 +08:00
    @timethinker 是的,这两个问题应该是清楚了。感谢!
    MiracleShadow
        9
    MiracleShadow  
       2022-06-29 11:58:11 +08:00
    函数有点复杂的,拆一拆然后测试呢?
    nothingistrue
        10
    nothingistrue  
       2022-06-29 12:16:43 +08:00
    单元测试不测试与它无关的东西,外部依赖一律 mock 。
    AS4694lAS4808
        11
    AS4694lAS4808  
    OP
       2022-06-29 13:34:41 +08:00
    @MiracleShadow 因为函数很短,就没想着改动。。思路不对。。
    filwaline
        12
    filwaline  
       2022-06-29 23:37:02 +08:00   ❤️ 1
    https://enterprisecraftsmanship.com/book

    个人推荐你看看这本书,全面讲解了单元测试是什么,以及怎么写好的测试。书籍要付费购买,但是非常值得。

    比如,你的问题,就是需要拆分的情况 (over complicated code),应用 humble object pattern 将其拆分为 domain model 和 controller ,然后针对 domain model 写 unit test ,如果有必要就为 controller 也写一个 intergeation test

    ![types-of-code]( https://drek4537l1klr.cloudfront.net/khorikov/Figures/07fig05_alt.jpg)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   960 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 20:36 · PVG 04:36 · LAX 13:36 · JFK 16:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.