面试程序员内功之测试篇

#test
at

我个人对于测试的一些回答和总结.

对于 TDD 的一些看法和实践?

正如Kent Beck所述,

I get paid for code that works, not for tests.

  • 写足够小粒度的测试, 避免付出大的测试代价. 用来保证核心模块正确性.
  • 确定函数的封装和粒度是切实合理的.
  • 确保同类问题不会犯第二次.

对于软件测试的一些看法?

我觉着对于开发而言, 软件测试最重要的好处是, 可以深刻理解什么叫做可测试性 testability. 软件测试环节可以显著的告诉开发, 究竟软件开发的(阶段性)结果应该是什么. 在常见的需求领域中, 除了功能性需求Should do, 还有很多非功能性的需求should be. 程序员只是具备解决定义清晰问题的能力是远远不够的. 理解需求, 跟不同人协作, 则是软件开发的另一个层面. 理解软件测试则给了这样一个机会.

对于{functional tests, integration tests, unit tests, acceptance tests, slow tests, fast tests, UI tests, E2E tests, and on and on}的概念和理解?

  • 测试的流派非常繁杂, 大部分概念都类似或重叠, 能够掌握所有的测试概念并没有太大意义.
  • 对于普通程序员而言, 更需要构建一个覆盖相对全面, 能够轻易理解和掌控的测试分层方法.
  • 我个人认为掌握 3 类就足够应付大部分场合.
  1. Unit Test
  2. Integration Test
  3. Performance Test 单元测试, 就是最基准的函数级别的测试. 我一般会要求所有的单元测试在 3s 内测试完毕. 集成测试 Integration Test, 主要希望测试模块间搭配的结果应该是怎样的. 我一般会希望所有的集成测试在10分钟内测试完毕. 其中我认为端到端测试 (End to End test) 是可以归纳到集成测试里面的. 对于程序员而言, 不需要太严格的理解e2e test. 另外关于测试比重, 推荐是7:2:1, unit test: integration test: e2e test. 参考: Just Say No to More End-to-End Tests

性能测试 (Performance test) 则是完全不同的应用场景. 但是掌握性能测试才是真正的理解了自己技术的瓶颈. 分析磁盘, 网络, 内存, CPU 等一系列内容来推测自己的代码瓶颈和真正需要改善的那部分. 这个可以讲很多, 在此先略过. 事实上有太多花哨名字的测试, 至少对于开发而言, 并没有本质上的区别. 同样精力下, 掌握繁杂的概念并不会改善代码质量. 如果你加入了一家术语跟你不一样的公司, 只需要映射到自己的概念里面就好.

另外, A/B Testing, 安全测试, 我认为都已经算是测试的引申. 超出了本篇的范畴.

stub and mock?

自从造词大婶 Martin Flower 写了一篇 Mocks Aren't Stubs". 大家就开始面试这种概念问题了. stub 主要是通过存储结果来进行反馈. 而 mock 则会模拟行为. 我个人认为明确的区分没有任何意义, 尽可能采用便于理解的方法才是王道.

推荐一些关于软件测试的入门书?

A Friendly Introduction to Software Testing Starting to Unit Test: Not as Hard as You Think by Erik Dietrich

关于面试程序员测试能力的过程和思考

如果在面试程序员, 问道对方测试相关的问题, 那肯定是希望:

  • 有全局观
  • 知道软件是如何协作的
  • 有优先级, 分类组织软件结构
  • 能够意识到测试往往是在理想条件下作出的

检验测试能力的常见手段分为四类:

测试某个真实世界中的物品 比如, 如何测试一个杯子? 这时候往往真是的意图是交流, 反复的交流. 中心点是究竟应该 如何测试 一个怎样的杯子. 场景: i for interviewer, c for candicate: i: 如何测试一个杯子? c: 大概谁会去用这个杯子呢?i: 可能是个小孩吧. c: 大概这个小孩年龄多大呢?i: 可能只有三个月大. c: 那我们可能需要一个奶嘴了. 杯子主要用来盛放水么?… c: 所以最终我们是需要测试一个用来盛放温热牛奶给三个月-六个月的婴儿使用的杯子. 看起来这个题目很具体, 然而真正需要测试的点可能是完全不同的. 测试软件的一部分 需要体系化, 有理有据的定位问题: 首先要确定是黑盒测试, 还是白盒测试.

  • 能够使用的工具有哪些?
  • 可能的边界
  • 压力测试和失败条件是什么?

常见的测试指标

  • Response time
  • Throughput
  • Resource Utilization
  • CPU/disk/memory Load

测试某一个函数

定义输入和输出. (这点非常重要, 往往没定义意味着忽略了重要的输入或者输出)

  • 正常的输入输出.
    • 边界输入输出, off-by-one issues.
    • NULLs, 0s.
  • 非法输入, 特殊的输入, 比如对于二分, 使用偶数个或者奇数个输入, 对于排序, 使用排列好的数组.- 明确定义期望的结果

分析解决一些现有问题, 需要问清楚问题的具体场景.

分解场景为具体的逻辑流. 比如处理一个REST服务, 可能会分解为: 接受一个REST请求, 解析请求, 加载数据库, 计算数据, 序列化结果, 返回结果. 创造使用针对性强, 易操控的测试方法.

对于随机问题的场景思考.

比如, 无法复现, 或者有的人可以复现, 有的人复现不了的时候. 可能的原因包含不限于:

  • 使用未初始化的变量
  • 内存泄漏
  • 使用随机量
  • 外部资源依赖 比如加载文件, 文件权限, etc.
« 谈谈时间片