实践中的重构25_UT也需要持续重构

本文通过一个具体案例展示了单元测试(UT)的重要性及如何随着需求变化进行重构。案例中,初始UT确保了代码正确性,面对需求变更,作者逐步优化UT以覆盖更多场景,包括边界条件,并减少重复代码。
UT是个好东西,在对代码进行持续重构的过程中,UT可以保证代码的正确性。同时,和产品代码一样,UT也是需要不断重构的。
有一个接口,原始定义和对应的UT如下:
	/**
* 构建一个按照自然周排好的日期列表(包括最近30天,不包含当日),周时间距离现在越近,在列表中越靠前。
*
* <pre>
* 保证每个子列表的大小都为7(一个星期的天数),没有日期则以null填充。
*
* 例子:
* 如果当前时间是2010-09-08(星期3)则构建的列表为:
* 第1个subList 2010-09-06(星期1),2010-09-07(星期2),null,null,null,null,null,
* 第2个subList 2010-08-30(星期1),2010-08-31(星期2),2010-09-01(星期3),2010-09-02(星期4),2010-09-03(星期5),2010-09-04(星期6),2010-09-05(星期日)
* ...
* 第n个subList null,null,null,null,date(星期5),date(星期6),date(星期日)
* </pre>
*
*
* @return 返回构建好的列表。
* */
public List<List<Date>> getDaysInWeekList();


	private DateService dateService;

@Before
public void setUp() {
dateService = new DateServiceImpl();
}

@Test
public void test() {
List<List<Date>> list = dateService.getDaysInWeekList();

Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, -30);

int count = 0;

for (int i = list.size() - 1; i >= 0; i--) {
List<Date> subList = list.get(i);
Assert.assertEquals(7, subList.size());
for (Date date : subList) {
if (date == null) {
continue;
} else {
Date tem = calendar.getTime();
Assert.assertEquals(DayUtil.getDayBeginString(tem),
DayUtil.getDayBeginString(date));
calendar.add(Calendar.DATE, 1);
count++;
}
}
}

Assert.assertEquals(30, count);
}

当需求变更时,UT理应做出对应的修改。
新的需求比较简单。
新加入一个参数userId,通过该userId如果可以找到一个UserInfo的话,则返回结果的日期截至点为该UserInfo的最后修改日期。由其他系统保证该修改日期在当前时间之前的某一天。
新的UT如下:
	private NewDateServiceImpl_0 newDateService;

@Before
public void initNewDateService() {
newDateService = new NewDateServiceImpl_0();

newDateService.setUserService(new UserService() {
@Override
public UserInfo findUserInfo(String userId) {
UserInfo userInfo = new UserInfo();
userInfo.setModifyDate(new Date());
return userInfo;
}
});
}

@Test
public void testGetDaysInWeekList() {
List<List<Date>> list = newDateService.getDaysInWeekList("allen");

Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, -30);

int count = 0;

for (int i = list.size() - 1; i >= 0; i--) {
List<Date> subList = list.get(i);
Assert.assertEquals(7, subList.size());
for (Date date : subList) {
if (date == null) {
continue;
} else {
Date tem = calendar.getTime();
Assert.assertEquals(DayUtil.getDayBeginString(tem),
DayUtil.getDayBeginString(date));
calendar.add(Calendar.DATE, 1);
count++;
}
}
}

Assert.assertEquals(30, count);
}

可以看到,UT是修改了,但是该UT只是在原有UT的基础上简单的修改了一下,设置了一个Mock的UserService。并且该处的日期设定和系统约束不符合,修改日期应该是早于当日的一个日期。
这样的UT当然是不理想的,当需求变更时,UT有可能要重新设计,而不是在原来的基础上打补丁。简单的分析可以得出,应该有3个分支需要覆盖。
1 找不到userInfo的情况,该情况对应原有功能。
2 找到userInfo的一般情况,返回的结果列表包含多个list。
3 找到userInfo的特殊情况,用户的最后修改时间和结果的起始时间比较接近,导致结果为1个list的情况。原有的接口因为可以保证正确的实现只能产出30个不为null的日期组装的结果,因此没有考虑该情况。但是随着需求的变更,该特殊情况需要纳入考虑范围。
	protected NewDateService newDateService;

private void setUpUserService(final UserInfo userInfo) {
newDateService.setUserService(new UserService() {
@Override
public UserInfo findUserInfo(String userId) {
return userInfo;
}
});
}

@Before
public abstract void initNewDateService();

@Test
public void testGetDaysInWeekListWithUserInfo_1() {

final UserInfo userInfo = new UserInfo();
userInfo.setModifyDate(DayUtil.addDays(new Date(), -2));

newDateService.setUserService(new UserService() {
@Override
public UserInfo findUserInfo(String userId) {
return userInfo;
}
});

List<List<Date>> list = newDateService.getDaysInWeekList("allen");

assertResult(list, 29);
}

@Test
public void testGetDaysInWeekListWithUserInfo_2() {

final UserInfo userInfo = new UserInfo();
userInfo.setModifyDate(DayUtil.addDays(new Date(), -28));

newDateService.setUserService(new UserService() {
@Override
public UserInfo findUserInfo(String userId) {
return userInfo;
}
});

List<List<Date>> list = newDateService.getDaysInWeekList("allen");

assertResult(list, 3);

}

@Test
public void testGetDaysInWeekListWithNull() {

setUpUserService(null);

List<List<Date>> list = newDateService.getDaysInWeekList("allen");

assertResult(list, 30);

}

private void assertResult(List<List<Date>> list, int expectedCount) {

Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, -30);

int count = 0;

for (int i = list.size() - 1; i >= 0; i--) {

List<Date> subList = list.get(i);

Assert.assertEquals(7, subList.size());

for (int k = 0; k < subList.size(); k++) {
Date date = subList.get(k);
if (date == null) {
continue;
} else {

Date tem = calendar.getTime();
Assert.assertEquals(DayUtil.getDayBeginString(tem),
DayUtil.getDayBeginString(date));
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
if (k != 6) {
Assert.assertEquals(k + 2, dayOfWeek);
} else {
Assert.assertEquals(Calendar.SUNDAY, dayOfWeek);
}

calendar.add(Calendar.DATE, 1);
count++;
}
}
}

Assert.assertEquals(expectedCount, count);
}

简单的一个修改如上,覆盖了3个主要的分支。
但是,修改完成后重新审视代码,发现还是存在一些明显的问题。
1该UT中有了重复的地方。
2主要的分支是覆盖了,但是没有考虑边界情况,即用户最后修改时间在起始时间之前。
3原有代码中,由于整数下标和Calendar中DAY_OF_WEEK的值不是对应的,因此需要转化,该处转化抽成一个小方法看上去比较好。
再次修改后代码如下:
	private final static int DAYS_OF_WEEK = 7;

protected NewDateService newDateService;

private void setUpUserService(final UserInfo userInfo) {
newDateService.setUserService(new UserService() {
@Override
public UserInfo findUserInfo(String userId) {
return userInfo;
}
});
}

@Before
public abstract void initNewDateService();

private void testGetDaysInWeekListWithUserInfo(int addDays,
int expectedCount) {

UserInfo userInfo = new UserInfo();
userInfo.setModifyDate(DayUtil.addDays(new Date(), addDays));

setUpUserService(userInfo);

List<List<Date>> list = newDateService.getDaysInWeekList("allen");

assertResult(list, expectedCount);
}

@Test
public void testGetDaysInWeekListWithUserInfoAll() {

testGetDaysInWeekListWithUserInfo(-31, 0);

for (int i = -30; i <= -1; i++) {
testGetDaysInWeekListWithUserInfo(i, 31 + i);
}

}

@Test
public void testGetDaysInWeekListWithNull() {

setUpUserService(null);

List<List<Date>> list = newDateService.getDaysInWeekList("allen");

assertResult(list, 30);

}

private void assertResult(List<List<Date>> resultList, int expectedCount) {

Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, -30);

int count = 0;

for (int i = resultList.size() - 1; i >= 0; i--) {

List<Date> weekList = resultList.get(i);

Assert.assertEquals(DAYS_OF_WEEK, weekList.size());

for (int k = 0; k < weekList.size(); k++) {
Date date = weekList.get(k);
if (date != null) {

Date tem = calendar.getTime();
Assert.assertEquals(DayUtil.getDayBeginString(tem),
DayUtil.getDayBeginString(date));

int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);

Assert.assertEquals(convertIndexToDayOfWeek(k), dayOfWeek);

calendar.add(Calendar.DATE, 1);
count++;
}
}
}

Assert.assertEquals(expectedCount, count);
}

private int convertIndexToDayOfWeek(int index) {
return (index + 1) % DAYS_OF_WEEK + 1;
}
内容概要:本文系统介绍了算术优化算法(AOA)的基本原理、核心思想及Python实现方法,并通过图像分割的实际案例展示了其应用价值。AOA是一种基于种群的元启发式算法,其核心思想来源于四则运算,利用乘除运算进行全局勘探,加减运算进行局部开发,通过数学优化器加速函数(MOA)和数学优化概率(MOP)动态控制搜索过程,在全局探索与局部开发之间实现平衡。文章详细解析了算法的初始化、勘探与开发阶段的更新策略,并提供了完整的Python代码实现,结合Rastrigin函数进行测试验证。进一步地,以Flask框架搭建前后端分离系统,将AOA应用于图像分割任务,展示了其在实际工程中的可行性与高效性。最后,通过收敛速度、寻优精度等指标评估算法性能,并提出自适应参数调整、模型优化和并行计算等改进策略。; 适合人群:具备一定Python编程基础和优化算法基础知识的高校学生、科研人员及工程技术人员,尤其适合从事人工智能、图像处理、智能优化等领域的从业者;; 使用场景及目标:①理解元启发式算法的设计思想与实现机制;②掌握AOA在函数优化、图像分割等实际问题中的建模与求解方法;③学习如何将优化算法集成到Web系统中实现工程化应用;④为算法性能评估与改进提供实践参考; 阅读建议:建议读者结合代码逐行调试,深入理解算法流程中MOA与MOP的作用机制,尝试在不同测试函数上运行算法以观察性能差异,并可进一步扩展图像分割模块,引入更复杂的预处理或后处理技术以提升分割效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值