mooctest全国大学生软件测试大赛开发者赛道相关知识记录(Junit 4)

环境要求
- JDK 8
虽然我只有JDK 17,改了下Language Level,保证不会使用新特性。但是还是改用jdk8了(后面会讲,变异测试需要jdk8) - Junit 4.12(在pom.xml中已指定好)
- 我用的是 IDEA,毕竟2024年mooctest比赛终于可以不用那个难用得要死的eclipse了
异常处理
1 | import org.junit.rules.ExpectedException; |
注意,一个Test方法只能测试一处异常。如果你想测试一个对象方法里会有的多个异常,还是只能分成多个测试方法分开测。
!!!!!可以自己实现assertThrows
其实有个允许一个Test方法内测试多个异常的函数,但是是Junit5中的。通过参考23年省赛答案,我发现了答案中对assertThrows主动进行了实现。
1 | private void assertThrows(Runnable r, Class<? extends Throwable> exception) { |
反射读写private字段
有些私有字段我们想主动访问而不是通过调用public方法间接访问,就直接用反射直接读写。
1 | Shop shop = new Shop(); |
反射调用私有方法
有些私有方法我们测试不到或者不能直接测试到,就可以通过反射直接调用。
1 | import java.lang.reflect.InvocationTargetException; |
assertEquals和assertSame
assertEquals
是使用 ==
进行比较,assertSame
比较两个引用是否指向堆上的同一个对象。
assertEquals(double)
主动提这个是因为IDE会有警告或者报错,要求传入第三个参数,即误差范围。
1 | void assertEquals(double expected, double actual, double delta) |
查看源码可以发现就是差<=误差就认为相等。
1 | Math.abs(d1 - d2) <= delta |
遍历枚举
懒得一个一个枚举的测试?直接遍历!
1 | // 定义 |
@Before和@After
有时我们想对于每个测试方法,都做同一个前置或后置操作(比如创建新对象、将变化的值恢复)。Junit 提供了对应的注解。
这里主要使用了@After和@Before。还有个@BeforeClass,它只在所有Test方法运行前调用一次,且这个注解所在的方法必须是静态方法(这个好理解吧,就是类初始化时调用一次这样)。@AfterClass在此比赛中没用,故不写了。
1 | import org.junit.After; |
输出为
1 | === before_class |
测试控制台输出
众所周知测试肯定不能修改待测代码,但是对于只有 sout 的 void 方法,我们肯定也是要测试。我们可以通过输出重定向的方法捕获输出内容到变量中,然后 assert。我的这种写法是在每个Test结束后恢复默认out,在部分需要重定向输出的方法内自行setOut。因为重定向后,就不能把自己的日志打印到终端上了。
1 | import org.junit.After; |
根据操作系统适配换行符与路径分隔符
为什么要根据OS适配?具体可以看CSDN的这篇文章:回车换行(CRLF)已过时,应废除!”SQLite 之父的公开呼吁引发热议
1 | String lineSeparator = System.getProperty("line.separator"); |
变异测试
变异测试的原理就是,把待测代码(在字节码层面改)改一下,如果我们的测试代码的 assert 依然通过(即变异子存活),那说明代码存在问题。比如我们测试 int subtract(int a,int b){return a-b;}
这个减法函数,如果我们的 junit 写的是 assertEquals(subtract(0,0), 0)
,那么变异子将subtract的减法改成加法,这个 assert 依然通过,说明咱们的测试存在疏漏。
我的理解是变异测试不是让开发人员自行对代码单元测试的,而是让测试人员完善测试用例的。
我们使用 pitest 这个工具进行测试。包在 pom.xml 里已经有了。
我们使用下面的命令启动测试。
1 | mvn org.pitest:pitest-maven:mutationCoverage |
注意如果版本 >= jdk9,会报错Unable to make field private final java.util.Comparator java.util.TreeMap.comparator accessible: module java.base does not "opens java.util" to unnamed module
所以就只能老老实实用 jdk8 了……
按下面这样配一个运行按钮,会方便一点。注意填入的命令是pitest:mutationCoverage
运行成功后,查看target/pit-reports/时间/index.html
即可
共享变量被修改导致变异测试失败
可能即便测 coverage 已经全部 pass 了,pitest 这里还是有报错All tests did not pass without mutation when calculating line coverage. Mutation testing requires a green suite. See http://pitest.org for more details.
可能是因为多个方法共享的变量(如静态变量等)被修改导致测试不通过(虽然我也不确定一定是这个问题导致的)。可以看下面的例子,各个Test方法是从上到下执行的,上面方法修改类变量cnt,会影响在下面方法里的测试。
1 | import org.junit.Test; |
变异测试详细学习
突变子列表,这个链接介绍了有哪些突变子以及对应做了什么突变。浏览一下可以方便我们看 index.html 里内容的时候知道是哪些变异子活下来了。
这些是运行 mutation 覆盖率测试时默认激活的 mutator:
1 | INCREMENTS_MUTATOR |
也可以自行配置其他突变子,这会导致测试时间变长,但能发现更多问题。(因为不知道比赛方是怎么配置的,不过这里不用深究,反正没空专门写变异测试)
1 | <configuration> |
在index.html中,悬停在红色数字上可以看到下面黄色区域的内容,就是对应行的存活变异子。
下面的例子就是说,第150行有两个变异子存活,即条件边界改变和条件取反。比如一个是变异成if(index < 2)
这种,一个是if(index > 0)
这种。我们就要针对这些情况再添加相关assert。
其他注意事项(来自2024省赛说明文件)
- 所提交的Java文件名需与代码中的类名保持一致。Java 语法要求代码文件名必须与代码类名一致,否则无法正常执行代码。有部分选手提交的文件名带有数字后缀,如“LibraryTest(1).java”,同样无法执行。
- 本届比赛要求选手将所有测试用例写在一个测试类中(最终提交一个 Java 文件)。所提交的测试类不要求必须为题目模板中的测试类,但需符合上述第1条要求。有考生写多个测试类并简单复制到同一类中,导致提交的测试类出现大量语法冲突。
- 由于变异测试的特殊性,所提交的所有测试用例均需要正常通过JUnit执行,不可有failure 或 error情况,否则将没有变异测试得分。
- 在算分的时候,将使用题目模板项目进行算分,这将导致根据修改过的源代码编写的测试代码可能无法通过执行。
- 由于慕测平台长时间不操作会自动退出登录,预选赛中有选手在结束前一分钟内提交答案时,被提醒登录“登录超时”,再次登陆后比赛已结束,导致未能成功提交答案。请各位参赛选手预留出充足的答案提交时间。
做题技巧
某些情况可以多用循环,增加覆盖率的同时,也更好杀死变异体。
先用 idea 的 diagram 看各个类的依赖关系,先覆盖最上层的类的方法,这样也会覆盖一些底层方法。如果先从底层类开始,会浪费时间。同时最好先提高覆盖率,再去弄变异测试,别花费时间在不提高分数的测试用例的编写上。
尽量避免修改共享变量,如果一定要修改,尽量改后还原(Test方法末尾还原或者在@After内处理)。
注意判题标准。反正24年的是下面这样。所以不要只做覆盖率。然后各个Test方法把名称和注释都弄得可读性搞些。运行效率的话,虽然我上面说了可以用循环,但是感觉只要不写死循环就OK吧。
注意是分支覆盖,所以不用浪费时间测试那些没有分支的函数如setter、getter
按以下五个维度进行评分。
- (30%)分支覆盖率:代码分支覆盖率。
- (30%)变异杀死率:参阅PIT工具网站指定的常见变异类型。
- (20%)可读性与可维护性:参阅各大企业的测试同样指南进行评分。
- (20%)脚本运行效率:针对该题为每个覆盖率区间给定一个基准时间,分数为(基准时间-运行时间)/基准时间。
脚本编写效率:总分=上述分数累加,总分相同则按提交时间二次排序。
可读性与维护性:可以使用
Alibaba Java Coding Guidelines
这个插件检查代码,或者直接用 idea 的 problems 面板查看。建议最后留五分钟改改代码可读性。
打完预选赛,根本做不完分支覆盖,可能是我太菜了。所以变异覆盖率也只能靠做分支覆盖时多加几种边缘值assert顺便提升。真的没空专门弄变异覆盖。
先把两个题目大概看下,可能第二题比第一题还容易。。。(指24年省赛的五星改为四星的第二题😓)
闲聊
- 青科大、广大、重邮应该是有加分,人嘎嘎多,把省一占满了都。
- 这个比赛争议挺大的,主要是软件测试没必要办比赛、认可度低、监管不严、可以用GPT这些。
- 个人感觉这个比赛挺抽象的,23年的省赛待测项目里没给test文件夹,一堆人临时学用eclipse建测试文件夹。24年省赛第二题一开始是五星,等我开始做第二题时发现变成了四星,难度比第一题低多了。然后允许用GPT,理论上来说是只不准用向日葵这种软件,反正赛中我看到个女生电脑屏幕界面上微信绿泡泡出现的时长比IDEA的时间久,又或者一个学生没主动写过一行代码,全程用的edge的侧栏copilot,是cv领域大神!真给我整无语了。考场的监管也挺松的,聊天软件不禁用就算了,最重要的是也没有签退机制和提交IP限制,我严重怀疑一些提前出考场的本校学生是回宿舍开黑了😓。
- 广东比陕西卷多了,我双75在陕西勉强拿了个省一,放广州顶多省二。又不加分又没报销,懒得去花1k拿个国三了。
- 如果能抽出三四天的空闲、代码理解能力强的可以来玩玩,这个比赛也不要怎么培训,全靠个人的经验和技术水平,拿个省一也够了。
赛题列表(欢迎热心同学一起整合历史赛题)
- 标题: mooctest全国大学生软件测试大赛开发者赛道相关知识记录(Junit 4)
- 作者: urlyy
- 创建于 : 2024-11-20 20:46:41
- 更新于 : 2025-03-16 01:04:15
- 链接: https://urlyy.github.io/2024/11/20/mooctest全国大学生软件测试大赛开发者赛道相关知识记录(Junit4)/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。