4/8 TDD测试驱动开发training
1. 代码整洁
-- 为什么要有整洁代码, 随着时间推移.....
-- 代码可读性: 命名,表达式(if-else,最好封装)
-- 方法行数控制,只做一件事
细节内容不需要暴露出来-提取方法
提取无关的子问题,确定好这个函数的high-level goal
-- 重复《程序员修炼之道》
-- Kent Beck写好代码的三要素:a.态度 b.技能 c.习惯
-- 注释 < 提取方法
-- 规模越小, 越通用越容易复用
重构:通用方法放到superClass
DRY原则: Don't Repeat Yourself
2. 面向对象: roles, responsibilities, collaborations
-- 判断职责: 职责操作的数据是谁的, 父类子类?
-- 隔离功能, 职责分配
-- 迪米特法则
-- 正交设计
a.消除重复
b.分离不同的变化方向
c.缩小依赖范围
d.向稳定的方向依赖
3. 测试加油站
单元测试不依赖于外部资源(文件系统,网络通信)
单元unit -- 业务场景
FIRST原则: a.Fast
b.Isolated
c.Repeatable
d.Self-verifying
e.Timely
单元测试文档化: 方法的命名, e.g. should_not_create_publication_in_offline_mode_when_click_no蛇形模式
GIVEN-WHEN_THEN
AssertJ:
-- assertThat()......
Mockito:
-- mock(X.class)
-- when(...).thenReturn(...);
-- mock的一个list曾经add过但是又clear了, verify也可以判断它有过这个行为。
测试的价值:
-- 1. 测试提供API调用的文档,阅读测试可以更有效地帮助我们理解开发代码
-- 2. 根据复杂的业务逻辑来先写测试, 可以保证逻辑代码的健壮性
慎用static方法,工具类才用
方法内不能new对象, 尽量注入(set或者spring注入)
不需要考虑coverage
-- 3. 被测试覆盖的代码可以更有效地保护重构的正确性
-- 4. 测试可以改进设计,保证代码的可测试性
4. 重构
外部条件不变的情况下, 优化内部设计
何时重构?
-- 新增功能
-- 修改bug
-- code review
-- 代码发现坏味道
函数对某个类的兴趣高于对自己的兴趣
......
重构手法
-- if分支语句: 合并提取公共因子
-- 属性list, 不让整个set, 可以add或者remove
-- 提取父类
-- split loop
把for循环中的职责分开。时间复杂度一样
练手(基本根据职责分配来重构):
提取当前类的因素, 不用getXX(),直接用XX, 因为用到了Movie的枚举类, 再次抽取方法, 移动到mivie中。
重构之前先run test, 改完再run test
问题: 一个系统中Unit Test框架,junit升级导致用不了。
4/21 TDD
1. 分解任务可以对业务逻辑进行分层
2. 伪代码
3. 再写测试案例
4. 根据测试案例开发
任务描述用语需要更具有业务风格
TEST: 报名人 Enrollee 报名单Ticket 报名通道EnrollingChannel
a. 验证报名(需要错误信息返回)
-- 1. 报名人资格核实(人和活动的部落一致性)
-- 2. 报名通道核实
-- 3. 不可重复报名
b. 完成报名(失败给原因:e.g.名额有限之类的)
c. 发通知(报名成功)
伪代码 -- 工具 ZenUML时序图
-- 点评: AppService中出现了多个对象的调用(业务逻辑)
不可, 这不属于网关层的任务,应该再嵌套一层EnrollService, 验证的流程都放里面
EnrollController.enroll(EnrollRequest) {
Enroll = EnrollRequest.to(); // 转换为领域对象
EnrollAppService.validateEnrollment(Enroll) {
EnrollerService.validate(Enroll);
EnrollingChannelService.isOpen(Enroll);
EnrollingChannelService.checkEnrollerNumber(Enroll);
EnrollRecordRepository.queryByEnrollerIdAndChannelId(Enroll);
}
EnrollRecordRepository.save(Enroll);
NotifacationClient.notify(Enroll);
}
-- 点评: 代码层面上注意AppService的职责边界: 只包含两项职责, 第一个是报名, 第二个是通知
总结
1. 每次改代码一定要跑所有的测试,起到了test对程序的保护
所以必须TDD+CI
2. 随时随地重构,在test的保护下。重构完也必须跑test
3. 保持TDD的结构感