单元测试的疑惑 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
AS4694lAS4808
0D
V2EX    Java

单元测试的疑惑

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



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



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



    之前单元测试写的少,都是比较好拆分的模块,最近要求测试模块详细完整了,所以有点摸不着头脑。。
    12 条回复    2022-06-29 23:37:02 +08:00
    zhazi
        1
    zhazi  
       2022 年 6 月 29 日
    测试是测试你的 [代码逻辑] 。跟数据库完全隔离的
    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 年 6 月 29 日
    既然他叫单元测试,还使用真实的上下游服务器数据库,这本身就是个错误
    timethinker
        3
    timethinker  
       2022 年 6 月 29 日
    这个不是单元测试吧,这个算是集成测试了,需要在独立的测试进程中对其进行测试,就好像是应用程序的真实使用者一样。

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

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

    不测试你不拥有的系统
    AS4694lAS4808
        5
    AS4694lAS4808  
    OP
       2022 年 6 月 29 日
    @zhazi sql 基本都是 mybatis-plus 搞的,倒是没有验证 sql 语句本身的需求。

    好像有点明白了,就是我应该把函数内部的 dao 和 mq 操作的 service bean 放到类属性,用 mockito 来生成,然后再调用方法测试吧?(现在 bean 是函数内从 ApplicationContext 获取的。。)
    之前主要是想非侵入的写个测试方法,看来钻牛角尖了。。
    非常感谢!
    28Sv0ngQfIE7Yloe
        6
    28Sv0ngQfIE7Yloe  
       2022 年 6 月 29 日   1
    你这里属于集成测试了,需要 mock 依赖的的服务、中间件,然后测试流程中插桩处理异常情况
    AS4694lAS4808
        7
    AS4694lAS4808  
    OP
       2022 年 6 月 29 日
    @yinhuochong6666 很有道理,之前没想明白怎么 mock ,现在想明白了。。谢谢!
    AS4694lAS4808
        8
    AS4694lAS4808  
    OP
       2022 年 6 月 29 日
    @timethinker 是的,这两个问题应该是清楚了。感谢!
    MiracleShadow
        9
    MiracleShadow  
       2022 年 6 月 29 日
    函数有点复杂的,拆一拆然后测试呢?
    nothingistrue
        10
    nothingistrue  
       2022 年 6 月 29 日
    单元测试不测试与它无关的东西,外部依赖一律 mock 。
    AS4694lAS4808
        11
    AS4694lAS4808  
    OP
       2022 年 6 月 29 日
    @MiracleShadow 因为函数很短,就没想着改动。。思路不对。。
    filwaline
        12
    filwaline  
       2022 年 6 月 29 日   1
    https://enterprisecraftsmanship.com/book

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

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

    ![types-of-code]( https://drek4537l1klr.cloudfront.net/khorikov/Figures/07fig05_alt.jpg)
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2938 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 48ms UTC 12:53 PVG 20:53 LAX 05:53 JFK 08:53
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86