JUnit断言错误排查:大输入量下预期异常未抛出问题咨询
问题分析与解决方案
为什么大输入未触发预期异常?
你的JUnit测试预期输入256、10000这类值时抛出异常,但当前代码没有触发,核心原因是输入大小的校验逻辑缺失:
当前buildTriangle方法仅校验了输入非空、无null值、大小远小于Integer.MAX_VALUE,但没有检查输入的元素数量是否刚好能填满你定义的锯齿三角结构。
从你的示例和countSize方法的逻辑来看,这个三角的元素总数是有固定规律的:当行数为rows时,总元素数是1+2+3+...+(rows-1)+1 = (rows-1)*rows/2 +1(比如rows=4时,总数是7,刚好匹配你的示例输入)。如果输入的元素数不等于这个固定值,就无法完整构建三角,此时应该抛出异常,但你的代码没有这个判断。
修复方案
修改countSize方法,让它同时返回三角能容纳的最大元素总数,然后在buildTriangle中校验输入大小是否等于这个总数:
private static int[] countSize(int listSize) { int rows = 0; int columns = 0; int maxElements = 0; int prevTotal = 1; int step = 0; // 替换原来晦涩的for循环为可读性更强的while循环 while (prevTotal + step <= listSize) { maxElements = prevTotal + step; prevTotal = maxElements; step++; rows++; } columns = 2 * rows - 1; return new int[] { rows, columns, maxElements }; }
然后在buildTriangle中添加校验:
int[] sizes = countSize(list.size()); // 校验输入大小是否刚好匹配三角所需元素数 if (sizes[2] != list.size()) { throw new myException("Input size does not match the required number of elements for the triangle"); }
这样当输入256、10000这类不匹配三角元素总数的值时,就会抛出异常,符合JUnit测试的预期。
代码评审建议
1. 异常处理优化
- 命名规范:
myException不符合Java驼峰命名法,应改为MyException。 - 保留异常上下文:
buildPyramid方法中捕获异常后直接抛出新的空参数异常,丢失了原异常的信息和栈轨迹,不利于调试。建议修改为:public int[][] buildPyramid(List<Integer> input) { try { return buildTriangle(input); } catch (MyException e) { // 保留原异常信息和栈轨迹 throw new MyException("Failed to build pyramid", e); } } - 冗余校验:
list.size()<Integer.MAX_VALUE - 8这个条件几乎不会触发(Integer.MAX_VALUE是2147483647,减8后仍远大于常规输入大小),可以移除。
2. 代码可读性提升
- 变量命名:
countSize中的x、y含义模糊,改为prevTotal、step这类有意义的名字(如上面修复方案中的写法)。 - 循环写法:原来的for循环
for(int x=1, y=0; x+y<=listSize; x=x+y, y++, rows++);过于晦涩,换成while循环更易理解。 - 代码格式:单行for循环建议加上大括号,比如:
for (int y=0; y<sizes[1]; y++) { System.out.print("\t" + resArray[x][y] + " "); } - 集合排序简化:
workList.addAll(list); Collections.sort(workList);可以简化为:
(记得导入List<Integer> sortedList = list.stream().sorted().collect(Collectors.toList());java.util.stream.Collectors)
3. 逻辑完整性
- 填充逻辑补全:你代码中的
//...sort logic...部分缺失,需要按照示例的结构填充排序后的元素到二维数组中。参考示例的位置规律:- 第
i行(0-based,非最后一行):从columns/2 - i位置开始,每隔2个位置放一个元素,共i+1个元素。 - 最后一行:仅在第0个位置放最后一个元素。
示例填充代码片段:
int elementIndex = 0; int mid = sizes[1] / 2; for (int i = 0; i < sizes[0]; i++) { if (i == sizes[0] - 1) { // 最后一行,仅填充第一个位置 resArray[i][0] = sortedList.get(elementIndex); elementIndex++; } else { int startPos = mid - i; for (int j = 0; j < i + 1; j++) { resArray[i][startPos + j * 2] = sortedList.get(elementIndex); elementIndex++; } } } - 第
4. 性能优化
- 空值检查效率:
list.contains(null)的时间复杂度是O(n),如果输入list很大,会影响性能。可以在排序或遍历填充时顺便检查空值,比如:
这样可以避免单独遍历一次list。List<Integer> sortedList = list.stream() .peek(num -> { if (num == null) { throw new MyException("Input list contains null elements"); } }) .sorted() .collect(Collectors.toList());
内容的提问来源于stack exchange,提问作者freekickr
相关产品推荐
相关产品推荐

