编程与软件工程tyrrrz.me
单元测试被高估了
Unit Testing Is Overrated
2020 — 2022,它一次次回到首页
黑点是上首页(≥ 50 分)的那次投稿,空心点是没冲上去的投稿,点越大分数越高。
导读
这篇文章讨论单元测试被高估的问题:并非所有代码都值得写单元测试,业务逻辑与大量“管道”代码应区别对待,后者往往更适合集成测试和系统测试。作者和评论者都强调,测试是工具而非目标,不应教条地追求 100% 覆盖率。
为什么经久不衰:它触及软件测试中经久不衰的争论,提醒开发者按代码类型、成本和实际收益选择测试策略,而不是盲目追求覆盖率指标。
HN 评论精选
把代码分为计算型和管道型,前者适合单元测试,后者更适合集成/系统测试。
我倾向于把代码大致分为两类:“计算型”和“管道型”。计算型代码处理业务逻辑,通常只占少数,但行为定义清晰,很适合单元测试,而且改动较少,测试能长期保值。管道型代码负责搬运信息,比如数据库访问、组件间传数据、传递用户输入等;给它写单元测试几乎没用,因为要 mock 一切、代码经常变、行为也不够明确。这类代码真正该测的是“它能不能工作”,交给集成测试和系统测试更合适。
chimprich查看英文原文
I tend to mentally divide code into roughly two types: "computational" and "plumbing". Computational code handles your business logic. This is usually in the minority in a typical codebase. What it does is quite well defined and usually benefits a lot from unit tests ("is this doing what we intended"). Happily, it changes less often than plumbing code, so unit tests tend to stay valuable and need little modification. Plumbing code is everything else, and mainly involves moving information from place to place. This includes database access, moving data between components, conveying information from the end user, and so on. Unit tests here are next to useless because a) you'd have to mock everything out b) this type of code seems to change frequently and c) it has a less clearly defined behaviour. What you really want to test with plumbing code is "does it work", which is handled by integration and system tests.
程序员对测试的态度常经历从轻视、被烧、过度测试到务实取舍的阶段。
作为一个曾经年轻自大的程序员(现在只是自大),我经历了常见的几个阶段:1)觉得测试浪费时间,我知道怎么写能工作的代码;2)因为没测试被烧得很惨,复杂系统一改就坏,没人敢帮忙;3)矫枉过正,狂热拥抱 TDD;4)发现编程变得繁琐无趣,小项目也要花一半时间写测试;5)终于明白,有些时候测试很好,有些时候测试得不偿失,没有放之四海而皆准的规则,我会在最有意义的地方测试并管理好预期。
claudiusd查看英文原文
I can't believe I'm wasting my time on another testing debate. Speaking as a formerly young and arrogant programmer (now I'm simply an arrogant programmer), there's a certain progression I went through upon joining the workforce that I think is common among young, arrogant programmers: 1. Tests waste time. I know how to write code that works. Why would I compromise the design of my program for tests? Here, let me explain to you all the reasons why testing is stupid. 2. Get burned by not having tests. I've built a really complex system that breaks every time I try to update it. I can't bring on help because anyone who doesn't know this code intimately is 10x more likely to break it. I limp to the end of this project and practically burn out. 3. Go overboard on testing. It's the best thing since sliced bread. I'm never going to get burned again. My code works all the time now. TDD has changed my life. Here, let me explain to you all the reasons why you need to test religiously. 4. Programming is pedantic and no fun anymore. Simple toy projects and prototypes take forever now because I spend half of my time writing tests. Maybe I'll go into management? 5. You know what? There are some times when testing is good and some times where testing is more effort than it's worth. There's no hard-set rule for all projects and situations. I'll test where and when it makes the most sense and set expectations appropriately so I don't get burned like I did in the past.
难写单元测试可能暴露的是 API 设计问题,而不是单元测试本身的问题。
也许问题不在于 mock 依赖,而在于试图掩盖 GetSolarTimesAsync 本来就需要日期和地点两个数据。原签名只接收日期,于是计算器得自己找地点,还要实现 IDisposable,以后支持其他地点、批量计算也会变复杂。如果把方法改成直接接收日期和地点,就不需要 location provider,也不需要 IDisposable,单元测试也变得简单。所以也许不是单元测试不好,而是难测试的代码在更广泛的应用里也可能难用。
DougBTX查看英文原文
Perhaps the problem isn't mocking dependencies, but trying to hide the fact that GetSolarTimesAsync needs two pieces of data to work: a date and a location. But the original signature is just this: public async Task<SolarTimes> GetSolarTimesAsync(DateTimeOffset date) That introduces a lot of complexity: * The SolarCalculator needs to be able to work out its own location, so it needs a LocationProvider * SolarCalculator needs to be IDiposable since it owns a LocationProvider * The SolarCalculator will need more methods if it ever needs to calculate the times in a different location * If fetching the location is slow, but the application needs to calculate times for multiple dates (eg to build up a table of times), then the SolarCalculator will need an method that takes in an array of dates to be efficient But all that could be solved by making the function take all of the arguments it needs to return its value: public SolarTimes GetSolarTimes(DateTimeOffset date, Location location) No location provider needed, no IDiposable, just one efficient stand-alone method. Unit testing this is now just: var calculator = new SolarCalculator(); var actual = calculator.GetSolarTimes(new Date(...), new Location(...)); var expected = new SolarTimes(...); actual.Should().BeEquivalentTo(expected); ...so, perhaps the issue isn't that unit testing is a bad idea, but that code which is hard to use in a unit test might also be hard to use in a wider application? And perhaps the fix is to make the code easier to use?
盲目追求 100% 单元测试覆盖率会拖慢交付、产生低价值测试,甚至掩盖真正的问题。
我所在的团队痴迷 100% 单元测试覆盖率,结果交付功能要花 3 到 5 倍时间,写出来的测试往往:1)其实更适合放进集成测试;2)没有真正回报,框架本来就处理了大部分问题,却花几百行去 mock;3)质量很差,先写代码再补测试,只为报告上显示 92% 覆盖率。我们的事故反而比之前全员资深工程师、不追指标的团队更频繁。想要高覆盖率就该认真做 TDD,或者干脆写职责单一、容易测试的简单代码,而不是这种半吊子追指标的做法。
me_me_mu_mu查看英文原文
Team I'm on obsesses with 100% unit test coverage which means that it takes 3-5x as long to ship a feature while engineers write tests that are: 1) Almost always better to be part of integration test 2) Offer no real ROI because the framework usually handles most of these issues gracefully to begin with, so its just a few hundred lines of cute code trying to mock out shit (or better yet getting multiple people in a zoom and wasting their time trying to test 4 lines of code - WHICH THE FKIN LIBRARY/FRAMEWORK ALREADY HANDLES) 3) Complete garbage. They write the code first and then add tests. That's fine if these were integration tests, but they run their little commands to print out a report that says 92% test coverage. Oh no No nO nO NO No meme-1.gif.jpeg. We have incidents regularly compared to my previous team, which was granted all Senior/Staff engineers and we didn't spend our time chasing dumbass metrics. If you want 100% unit test coverage, just do TDD and not this half-ass yolo cowboy approach. Or just write simple, single responsibility code so it can be tested easily. But nah, you know how it is. These people are annoying as hell to work with, and unfortunately I work at a bigger company so this kind of BS is normal as people try to chase promotions instead of actually shipping quality products. I personally wouldn't hire people like this, nor keep them around this long.
历次讨论
同类还值得读
开源应用架构
The Architecture of Open Source Applications
《开源软件架构》(AOSA)是一本在线免费书,邀请知名开源项目的作者亲自剖析其内部设计,涵盖 SQLAlchemy、LLVM、Audacity、Apache、Bash 等系统。
编程与软件工程上首页 9 次 · 2011–2024aosabook.org编辑精选编程语言简史:简短、不完整且多半错误
A Brief, Incomplete, and Mostly Wrong History of Programming Languages
一篇以戏谑、故意不准确的方式回顾编程语言史的博客文章,用夸张轶事和讽刺定义串起 Lisp、C、C++、Java、JavaScript、PHP 等语言。
编程与软件工程上首页 8 次 · 2009–2021james-iry.blogspot.comSQLite 是如何测试的
How SQLite is tested
SQLite 官方文档页,系统介绍 SQLite 的测试体系:多种测试方法(TCL 测试、模糊测试、回归测试、静态分析等)和海量测试用例,展示了这个小型开源项目如何靠极致测试获得…
编程与软件工程上首页 8 次 · 2009–2025sqlite.org编辑精选高级编程语言(2009)
Advanced programming languages (2009)
Matt Might 的经典文章,按用途推荐“高级编程语言”:如果追求就业就选主流语言,如果追求表达力、学术研究或创业,则推荐 Haskell、OCaml、Scala 等函数式语…
编程与软件工程上首页 8 次 · 2009–2022matt.might.net我的 Forth 与栈机器经历
Yosefk - My history with Forth & stack machines
作者以亲身经历回顾了使用 Forth 语言和栈式机器的历程:既赞叹其简洁优雅、代码量小、可自举的优点,也坦率指出栈操作在实际编程中的痛苦,以及 Forth 社区文化中的问题。
编程与软件工程上首页 8 次 · 2010–2024yosefk.com编辑精选