最近看了极客时间的 [ 10x 程序员工作法] 和 [设计模式专栏] ,里面有一些关于单元测试的内容, 看完之后,感觉好像懂了,但是回头一看自己写出的代码,却不知道如何下手。
想请教一下大家,日常工作中是如何写单元测试的,团队的规范是怎样的,怎么迈出单元测试的第一步!!!
1
lhx2008 2021-05-16 23:00:35 +08:00
面向对象设计,把代码分层好,这样底层就可以 mock 住,这样就可以开始跑单测了
|
2
Jooooooooo 2021-05-16 23:00:53 +08:00
和你说个秘密, 这些都是书上说的好听.
|
3
XiLemon OP |
4
lhx2008 2021-05-16 23:07:59 +08:00
@XiLemon #3 如果是 SQL CURL,可以考虑直接上集成测试吧,或者后端改成用临时数据库( h2, sqlite )来本地跑,不用 mock 了,如果涉及到到调出到外部的 API 再考虑 mock
|
5
Jooooooooo 2021-05-16 23:19:00 +08:00
@XiLemon 还有诸如 DDD, RESTful 之类的东西你会发现都是说的好听, 实践几乎没有的.
|
6
fpure 2021-05-16 23:22:36 +08:00
@Jooooooooo 确实,实践这一套的成本不低
|
7
aragakiyuii 2021-05-16 23:25:01 +08:00 via iPhone
我感觉得先写出来能写单测的代码...然而现实情况是由于时间问题能跑就行
|
8
XiLemon OP @lhx2008 谢谢
@Jooooooooo 0.0.. 没有实践过。。。 @fpure 不能光看成本呀,收益如何呢 @aragakiyuii 现实就是写的业务代码,对单测很不友好。假设时间够,我该怎么写出单测友好的代码呢,光看一些简单的例子,领悟不了 ─.─|| |
9
ClericPy 2021-05-17 00:27:52 +08:00
之前也强制自己尝试 TDD DDD 什么的, 后来发现自己抽象的不好是原罪... 不过单元测试一直写的比较完整, 所以至今没出过大问题, 而且每次上线前被测试拦住都有一种劫后余生的畅快
|
10
mxalbert1996 2021-05-17 00:37:52 +08:00 via Android
@lhx2008 事实上 mock 并不是一个很好的解决方案,如果可能的话应该尽量避免写出需要 mock 才能测试的代码。具体可以看这个:
https://android.googlesource.com/platform/frameworks/support/+/androidx-main/docs/do_not_mock.md |
11
ccde8259 2021-05-17 00:49:21 +08:00 via iPhone 1
什么 TDD DDD 最后都要败给 DDL……
能上,能用,能赚钱才是王道。 单测首先 Mock 环境就够喝一壶。Mockito 还是 PowerMock 得引入。H2 和 RedisMock 都要折腾。还有 RPC 需要 Mock…… 然后就是真正开始准备编码,单测里面你会发现自己代码根本没法单测……一个接口设计的不合理就要改接口来满足单测。具有上下文依赖的接口是不是要改显式注入?接口过于万能引起测试用例过多是不是需要拆分?代码覆盖率偏低是不是要调整? 坑太多收益也很一般。多数应用生命周期很短,过功能测试就上。用完就扔导致多数单测都是赔本买卖。 |
12
XiLemon OP |
13
hallDrawnel 2021-05-17 01:28:51 +08:00
单元测试要求在开始写代码的时候,就需要考虑可测试性,还得准备一堆基础设施才行,例如单元测试的时候日志系统和配置系统要能够更方便支持单元测试的场景,要比 mock 做得更方便才行,不能什么都靠 mock,至少基础设施要对单元测试友好。遗憾的是很多项目开始就没有考虑过,后期引入就代价很大。而且很多代码生命周期太短了,没啥测试的价值,引用个需求文档后期要改有个参考就行。
|
14
BeautifulSoap 2021-05-17 01:45:31 +08:00 via Android
单元测试最头痛的还是数据库怎么办
很多人都说 mock,但是我感觉实际上这么做对数据库来说并不是个好方法(坑一大堆) 所以一般业务代码测试我都是直接用实际的数据库来做的(主要针对 repository 层,java 似乎叫 dao ?),在每个测试开始前都清空下测试用数据库就行了 至于上层的测试,如 service 的测试里要不要 mock 掉 repository 接口之类的,我还不能得出明确的结论。可能 mock 掉比较好,不受下层实现的限制,但是有时候一些单元测试要 mock 的接口实在太多了,而且各种接口的依赖性非常复杂的话,光是 mock 都是个体力活,还不如直接用。 |
15
ericgui 2021-05-17 03:37:52 +08:00
业务变动比较大,怎么测?今天写了 2 个 test case,明天业务变了,这俩 test cast 就要删了,这不是浪费时间么
|
16
vemier 2021-05-17 08:50:02 +08:00
尽量使用集成测试代替多层的单元测试就好了,复杂的功能可以另外加单测。这样后期如果重构的话,测试用例完全不用改动,也少了很多意义不大的 Mock 。
|
17
bsg1992 2021-05-17 09:13:15 +08:00
业务代码很难进行单元测试的。
数据库没法进行测试,引入 InMemory DB 对数据库达不到测试的要求。 如果要介入数据库还得写插入和清库的代码。 第三方依赖也是一个问题。 到最后你会发现,写业务代码一个小时,单元测试得写上一天 |
18
witcherhope 2021-05-17 09:18:21 +08:00 via iPhone
如果团队没实行没规范,独狼很难做下去,团队有人推动,单测保证高覆盖率收益还是很大的
|
19
janxin 2021-05-17 09:18:23 +08:00 via iPhone
什么 TDD 还是 BDD 之流,最终还是落地到 ADD
|
20
leafre 2021-05-17 09:29:03 +08:00 5
楼上这些单元测试是什么都没弄清楚
|
22
no1xsyzy 2021-05-17 09:42:12 +08:00
1. 需要引数据库就不是单元测试了,已经算集成测试了。
2. 其实单测是 Write Everything Twice 的思想。 话说 you-get 这个项目,提需求是发个 PR,其中包括一个会 fail 的单测 |
23
cxshun 2021-05-17 09:47:47 +08:00
@XiLemon mock 对象没啥问题,因为一些逻辑有可能是别人写的,你不关注,那就直接 mock 解决,返回你希望返回的数据就好了。至于对方返回有问题,那这是集成测试需要做的东西,单元测试仅关注你自己关注的那块就好了
|
24
lightjiao 2021-05-17 09:55:59 +08:00
1. 自动化测试确实好用,特别是业务场景越来越复杂的时候,能够指数级的降低测试成本
2. 代码里把一个类所有依赖的第三方对象全都以构造参数的形式传递,方便 mock,比如 ``` class Download { private Http m_HttpClient; public Download(Http httpClient) { m_HttpClient = httpClient; } } ``` 3. 每个类自动化生成 mock 类,比如自动生成 HttpMock 、DownloadMock,这样在单元测试时可以方便的吧 mock 对象代替实际的对象传入做测试,这一步主要是单元测试框架完成的,测试时 mock 好每个依赖的 API 的输入输出即可 这种做法可以做到 100%代码行数覆盖的单元测试,实际要完成覆盖多少,根据项目情况来吧 |
25
11ssss 2021-05-17 10:06:05 +08:00
楼上不点名说了,这些确实是有成本,但是代码质量和接口健壮性甚至对开发者素质是有显著得提升的。但是你自己见识少没见过,不代表没人用没人落地,说话太绝对了。
|
26
timi 2021-05-17 10:07:32 +08:00
我们推过一段时间单元测试,后来为了单元测试而测试,个人以为核心代码单元测试就得了,CRUD 用接口测试保障就可以了,弄一大坨屎一样的单元测试代码,不好看也不好维护
|
27
ebingtel 2021-05-17 10:12:44 +08:00
有了测试用例 项目维护起来 得心应手……时间成本随着迭代,越来越低
|
28
www5070504 2021-05-17 10:13:02 +08:00
真的是方法论 我之前也特别相信这个东西,但是实际上是很难真正去实践的,
如果要实践 TDD 测试代码要来回重构不说,需求文档那边就得写的详细, 但是现在这个行情,你看看有几家能把需求文档写明白的 |
29
crazyhorse 2021-05-17 10:14:39 +08:00
做 feature test 比较好,刚开始很慢。坚持一段时间你会发现写代码效率高了不少,bug 更少,自己也不用去手动录入数据来做自测
|
30
ChristopherWu 2021-05-17 10:19:34 +08:00
|
31
GoLand 2021-05-17 10:24:43 +08:00
学习一下这个,面向 interface 编程,这样会发现单元测试很好写。
https://github.com/bxcodec/go-clean-arch |
32
qwerthhusn 2021-05-17 10:30:58 +08:00
之前公司突然要求单元测试代码覆盖率,于是我精通了 Java 的 PowerMock,Mockito 的使用,我甚至能把代码行和分支覆盖率提升到接近 100%,但是到了线上还是有 BUG 。、。。
|
34
sdushn 2021-05-17 10:35:54 +08:00
看完[ 10x 程序员工作法]会获得[ 10x 薪资不] (手动狗头保命)
|
35
CHANGEX929 2021-05-17 10:36:36 +08:00
把涉及到其他服务的部分都 mock 掉,数据库、缓存、远程 api 之类的。
|
36
RedisMasterNode 2021-05-17 10:44:22 +08:00
安利一下腾讯写的 Golang 的单元测试指引
https://mp.weixin.qq.com/s/eAptnygPQcQ5Ex8-6l0byA |
38
XiLemon OP @no1xsyzy 我去了解一下
@cxshun 我感觉可能是现在的方法写的太大了,一个方法做了很多事情,导致许村 mock 多个对象,导致单测很难写 @www5070504 我理解需求文档没写明白的好,最后咋移交的,怎么开发的呢? @sdushn 不一定会,但能让自己变成一个靠谱的选手 @RedisMasterNode 谢谢,我看一下 |
39
fewok 2021-05-17 13:46:47 +08:00
1 、你要有技术方案
2 、你要有上下游入口和出口定义 3 、基于入口和出口,搞定依赖、搞定场景数据准备、搞定入口调用 4 、边写业务代码,边写集成测试,或者特变关键的 util,可以专门测试下 5 、基于方法的单测,有一个是一个,我都认为是菜鸟 |
40
qiumaoyuan 2021-05-17 13:50:36 +08:00
我的理念是不知道做了有什么用的事情就不去做。所有的问题在你对代码质量、开发效率精益求精的过程中自然会遇上,然后再找解决办法。方法是用来解决问题的,没有问题就无所谓方法。很多人研究方法都是在自己没有遇到问题的前提下进行,自然一头雾水。
想想不写测试代码的时候,你是如何人工测试的。为什么有些地方你会进行人工测试,有些地方又很自信很放心的不测试? 代码就是把重复性的事情一次教会给电脑,让电脑去做重复劳动,测试代码也一样。 我觉得结合这两点,基本上就能理出写单元测试的动机和时机。先别管什么 TDD 不 TDD,饭一口一口吃。 |
41
no1xsyzy 2021-05-17 13:53:49 +08:00
@XiLemon 我发现我笔误了,是会 fail 的测试而不是单测。不一定是单测,而且通常来说它的需求是解析某网址,显然不应该是单测。
不过我凭空推测,它估计不是因为 TDD,而是因为 Issues 被广告机爆了。 |
42
kaedea 2021-05-17 13:54:29 +08:00 via Android
用来调试 API
|
43
qiumaoyuan 2021-05-17 14:58:39 +08:00
补充一下 #40,其实还有很关键的一点:很多人业务代码没有能力写干净,写测试代码就一定会更加添乱,这种人写测试往往都会半途而废。
|
44
wangyzj 2021-05-17 15:04:51 +08:00
楼上老哥有几个写单元测试的?
业务的变动和要求,你根本没时间写 也就面试的时候会被问到 理想和现实的差距 |
45
gdtdpt 2021-05-17 15:18:26 +08:00
首先自身业务代码层次要清晰,各层级间耦合度要低,这样在 Mock 的时候工作量才会少,才能专注测试的内容,不然光写 Mock 就一堆代码。
在一个 Service 方法里调用另外 3 、4 个 Service,然后又调用几次 Dao 不同的方法这种流水帐一样的代码写 Mock 都要写死人,怎么让人写单元测试。 |
46
angmieee 2021-05-17 16:04:22 +08:00
业务代码不写单元测试。你是嫌加班不够多,还是工作不饱和?
|
47
passerbytiny 2021-05-17 16:12:50 +08:00 via Android 1
单元测试有两个大前提:
一、你不一定要 TDD (先写测试代码),但测试框架的设计一定要早于主代码框架的——单元测试是领导而不是擦屁股的。 二、不一定是大项目,但一定是长期或者计划长期维护的项目——非自动化需要大量执行成本 /自动化需要大量设计成本,你不该为了半年甚至一天后就抛弃的项目做单元测试。 |
48
ClericPy 2021-05-17 21:15:32 +08:00
@ClericPy 我这边业务不是太复杂, 有时候单元测试有时候简单的功能测试, 太细致的单元测试就连 TDD 那本书作者都不建议 ( https://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests , 也就是著名的那句 "I get paid for code that works, not for tests")
有的同事天天加班到十一点, 进度天天催, 根本没时间写完整测试, 连文档都不愿意看, 最后布一套测试环境跑通核心功能验收就上线了. 毕竟对绝大多数公司来说, 开发人力根本严重不足, 方法论什么的行不通 总结起来就是, 上线前一定要有测试, 但是不要迷信 100% coverage. |
49
searene 2021-05-17 22:35:58 +08:00
我一般只写集成测试,数据库也不 mock,直接去读,这种方法很少有人提倡,但是在实际的业务开发中是性价比最高的,既省时间又能保证程序的基本功能。传统测试理念要求一个功能写一个单元测试,甚至写功能之前就要写测试,比如 TDD,这样做得话听起来很不错,但是实际开发中根本跟不上需求的进度
|
51
witcherhope 2021-05-18 11:56:42 +08:00
单测非常有必要,单测也是某种意义上的代码文档,在接手别人代码和重构时候就会知道单测意义所在了
|
52
godall 2021-05-18 12:58:41 +08:00
@qwerthhusn 好奇问下,怎么方法能把测试覆盖率提高到将近 100%?
|
53
ljf 2021-05-18 13:52:45 +08:00
我司要求单侧覆盖超过 80%,代码才允许合并,所以慢慢变成为了写好单元测试而写代码,大部分时间都在写单元测试
|
54
lix7 2021-05-18 19:39:04 +08:00 1
小型工具方法写单测,业务逻辑写集成测试。
业务逻辑写集成测试,mock 掉外部依赖(外部接口一类的),数据库不 mock,可以看下 testcontainers 项目,在测试部分启 MySQL / Redis 等容器。 |
55
XiLemon OP |
56
RandomJoke 2021-05-19 16:45:02 +08:00
从一些简单逻辑开始呗:
1. 比如 API 的输入 check 2. 比如相对简单但是共用的 util 一般来说,你觉得 test 不知道怎么写,要么是代码实在写的有问题,要么就是本身就不该写,不过大部分不知道怎么写都是代码的问题,可能层次划分的不对,逻辑拆分的不够细等等 |
57
MarioLuo 2022-03-19 11:47:57 +08:00 via Android
一年过去了,楼主单元测试有值得方向的经验吗
|