问题一、对于外部依赖的方法,应该mock数据,还是依赖具体执行?
应该mock数据
问题二、如果单元测试代码覆盖率已经到了100%,还需要补充所有测试场景吗?
覆盖率已经到了100%和所有的测试场景是两个不同的指标;
根据具体情况决定需要测试的场景,可将代码覆盖率作为参考
一、单元测试的标准:
- 独立性:每个单元测试应独立运行,不依赖其他测试或外部状态
- 可重复性:测试结果应一致,无论何时何地于行
- 自动化:测试应能自动执行,无需人工干预
- 快速反馈:测试应快速运行,以便及时发现问题
- 覆盖全面:测试应覆盖代码的主要路径,边界条件和异常情况
- 可维护性:测试代码应易于理解和维护
二、写好单元测试的关键点:
- 明确测试目标:每个测试应专注于验证一个特定功能或行为
- 使用描述性名称:测试方法应清晰描述其目的
- 遵循Arrange-Act-Assert 模式:
- 测试边界条件:确保测试覆盖输入的最大值,最小值和异常情况
- 模拟外部依赖:使用Mock或Stub隔离外部依赖,确保测试只关注被测方法
- 验证异常:确保在预期抛出异常时,测试能够捕获并验证
- 保持简洁:测试代码应简洁明了,避免复杂逻辑
- 定期维护:随着代码变化,及时更新测试用例
三、需要列举全部场景吗?还是代码语句覆盖率到达100%即可?
在编写单元测试时,代码覆盖率100%并不等于测试了所有场景,虽然高覆盖率是一个好的指标,但它并不能完全保证代码的健壮性,以下是两者的区别以及如何平衡它们的建议
-
代码覆盖率100%的含义
- 语句覆盖率:保证每一行代码都被执行
- 分支覆盖率:保证每个条件分支(如if-else,switch)都被覆盖
- 路径覆盖率:确保所有可能的执行路径都被测试
优点
- 快速发现未执行的代码
- 确保代码的基本逻辑被测试
局限性
- 即使覆盖率达到100%,也可能遗漏某些边界条件或异常场景
- 无法保证测试的深度和质量
-
列举全部场景的含义
- 边界条件:测试输入的最小值、最大值、空值等。
- 异常场景:测试代码在异常情况下的行为(如输入非法值、外部依赖失败等)。
- 业务逻辑:确保所有业务规则都被正确验证。
优点:
- 更全面地覆盖代码的实际使用场景。
- 提高代码的健壮性和可靠性。
局限性:
- 可能增加测试的复杂性。
- 某些场景可能难以完全列举,尤其是涉及外部依赖或复杂逻辑时。
-
如何平衡覆盖率与场景测试
- 优先覆盖核心逻辑
- 确保主要功能和核心业务逻辑被充分测试
- 对于次要功能或边缘代码,可以适当降低测试优先级
- 关注边界条件和异常场景
- 即使代码覆盖率已经很高,也要特别关注边界条件和异常情况
- 例如:测试空输入、非法输入、极端值等
- 使用代码覆盖率作为辅助工具
- 覆盖率工具可以帮助发现未测试的代码,但不能完全依赖它
- 在达到高覆盖率后,进一步补充场景测试
- 避免过度测试:
- 不需要为每一行代码都编写测试,尤其是简单的getter或setter方法
- 重点测试复杂逻辑和关键路径
- 优先覆盖核心逻辑
四、业务场景是否需要全部列举
是否需要列举所有的业务场景取决于多个因素,包括业务复杂性、风险容忍度、时间和资源限制等。
-
什么情况下需要列举所有的业务场景
- 业务逻辑复杂:
- 如果业务逻辑设计多种条件、规则或状态,列举所有场景可以帮助代码的正确性
- 例如:电商平台的折扣规则可能因用户等级,商品类别、促销活动等不同而变化
- 高风险场景
- 如果某些场景的失败会导致严重后果(如财物损失,安全漏洞),必须优先覆盖这些场景
- 例如:支付系统中的金额计算或权限验证
- 核心功能
- 对于系统的核心功能,必须确保所有可能的场景都被覆盖
- 例如:搜索引擎的查询逻辑或推荐算法
- 合规性要求
- 如某些行业(如金融、医疗、法规)可能要求对所有业务场景进行严格测试
- 业务逻辑复杂:
-
什么情况下不需要列举所有业务场景?
- 简单业务逻辑
- 业务逻辑非常简单,覆盖主要场景和边界条件可能就足够了,例如:一个简单的计算器应用
- 低风险场景
- 如果某些失败的影响较小,可以适当降低测试优先级
- 时间和资源有限
- 场景难以完全列举
- 如果业务场景过于复杂或动态变化,可能无法完全列举
- 例如:涉及用户生成内容(UGX)或机器学习模型的系统
- 简单业务逻辑
-
如何平衡业务场景的覆盖
- 优先级划分:根据业务重要性和风险等级,将场景分为高,中,低优先级
- 使用等价类划分和边界值分析
- 将输入数据划分为等价类,选择典型值进行测试
- 特别关注边界值,因为它们更容易出错
- 持续补充测试用例:
- 随着业务发展和新需求的出现,逐步补充测试用例
- 例如,当发现生产环境中的bug时,将其转化为测试用例
五、场景分析
假设有一个电商平台的折扣计算逻辑,规则如下:
- 普通用户:无折扣。
- VIP 用户:订单金额超过 100 元时享受 10% 折扣。
- 促销期间:所有用户享受 5% 折扣。
测试场景设计
-
普通用户:
- 订单金额 50 元,无折扣。
- 订单金额 150 元,无折扣。
-
VIP 用户:
- 订单金额 50 元,无折扣。
- 订单金额 150 元,享受 10% 折扣。
-
促销期间:
- 普通用户,订单金额 150 元,享受 5% 折扣。
- VIP 用户,订单金额 150 元,享受 10% 折扣(VIP 优先)。
测试代码
import
import org.junit.Test;
public class DiscountCalculatorTest {
@Test
public void testRegularUserNoDiscount() {
assertEquals(50, DiscountCalculator.calculateDiscount(50, false, false), 0.001);
}
@Test
public void testRegularUserNoDiscountHighAmount() {
assertEquals(150, DiscountCalculator.calculateDiscount(150, false, false), 0.001);
}
@Test
public void testVIPUserNoDiscountLowAmount() {
assertEquals(50, DiscountCalculator.calculateDiscount(50, true, false), 0.001);
}
@Test
public void testVIPUserWithDiscountHighAmount() {
assertEquals(135, DiscountCalculator.calculateDiscount(150, true, false), 0.001);
}
@Test
public void testPromotionRegularUser() {
assertEquals(142.5, DiscountCalculator.calculateDiscount(150, false, true), 0.001);
}
@Test
public void testPromotionVIPUser() {
assertEquals(135, DiscountCalculator.calculateDiscount(150, true, true), 0.001);
}
}
是否需要覆盖所有场景?
-
如果这是核心功能且涉及金额计算,建议覆盖所有场景。
-
如果时间和资源有限,可以优先测试 VIP 用户和促销期间的场景,因为它们的业务影响更大。