JML理论基础与工具链
JML简介
JML(Java Modeling Language)是一种对Java程序进行规格化设计的表示语言,定义了Java程序中的方法规格和类型规格,便于代码实现,展开测试和提高代码的可维护性。
JML语法介绍
JML主要以Javadoc的方式嵌入Java代码中,以@开头。
原子表达式
\result
表示一个非void类型的方法执行后的返回值;
\old(expr)
表示一个表达式在方法执行前的值;
\not_assigned(x,y,...)
表示括号中的变量是否在方法执行过程中被赋值;
\not_modified(x,y,...)
表示括号中的变量在方法执行期间其值未发生变化。
量化表达式
\forall
全称量词修饰,表示指定范围的每一个元素都满足相应的约束;
\exists
存在量词修饰,表示指定范围的某一个元素满足相应的约束;
\sum
返回给定范围内的表达式的和;
\max
与\min
返回给定范围内的最值。
操作符
b_expr1<==>b_expr2
与b_expr1<=!=>b_expr2
等价关系操作符,操作符两边都是布尔表达式;
b_expr1==>b_expr2
与b_expr1<==b_expr2
推理操作符;
\nothing
与\everything
变量引用操作符,表示当前作用域访问的所有变量,前者为空集,后者为全集。
方法规格
requires P
前置条件,即要求条件P为真;
ensures P
后置条件,即要求方法执行返回结果一定满足条件P;
assignable
与modifiable
副作用限定,分别表示可赋值,可修改。
JML相关工具链
- JMLUnitNG: 根据JML自动生成测试文件
JMLUnitNG的部署与使用
-
部署工作
官网下载的解压会出问题,选择在GitHub上下载:https://github.com/OpenJML/OpenJML/releases/
解压后,将三个jar包和下载的JMLunit的jar包与Solvers-windows文件夹放在同一个文件夹openjml里;
在src文件夹下添加该openjml文件夹; -
需要注意的点
在运行之前,需要对代码进行稍许修改。
0 <= i < groups.length
这种需要修改为0 <= i && i < groups.length
;
new ArrayList<>()
这种需要补全,如new ArrayList
()
下图出现的52
,51
是版本问题,不需关心。
-
使用
在src文件夹下 git bash,指令为:java -jar $jmlunitng.jar的路径 $被测试文件的路径 $其他文件的路径
针对本作业的 MyGroup.java文件,指令为java -jar openjml/jmlunitng.jar com/oocourse/spec3/main/MyGroup.java com/oocourse/spec3/*
生成测试文件后,运行MyGroup_JML_Test.java -
结果
[TestNG] Running:
Command line suite
Failed: racEnabled()
Passed: constructor MyGroup(-2147483648)
Passed: constructor MyGroup(0)
Passed: constructor MyGroup(2147483647)
Passed: <>.addPerson(null)
Passed: <>.addPerson(null)
Passed: <>.addPerson(null)
Passed: <>.delPerson(null)
Passed: <>.delPerson(null)
Passed: <>.delPerson(null)
Passed: <>.equals(null)
Passed: <>.equals(null)
Passed: <>.equals(null)
Passed: <>.equals(java.lang.Object@27082746)
Passed: <>.equals(java.lang.Object@7bfcd12c)
Passed: <>.equals(java.lang.Object@24273305)
Passed: <>.getAgeMean()
Passed: <>.getAgeMean()
Passed: <>.getAgeMean()
Passed: <>.getAgeVar()
Passed: <>.getAgeVar()
Passed: <>.getAgeVar()
Passed: <>.getConflictSum()
Passed: <>.getConflictSum()
Passed: <>.getConflictSum()
Passed: <>.getId()
Passed: <>.getId()
Passed: <>.getId()
Passed: <>.getPeopleSum()
Passed: <>.getPeopleSum()
Passed: <>.getPeopleSum()
Passed: <>.getRelationSum()
Passed: <>.getRelationSum()
Passed: <>.getRelationSum()
Passed: <>.getValueSum()
Passed: <>.getValueSum()
Passed: <>.getValueSum()
Passed: <>.hasPerson(null)
Passed: <>.hasPerson(null)
Passed: <>.hasPerson(null)
===============================================
Command line suite
Total tests run: 40, Failures: 1, Skips: 0
===============================================
通过运行结果可以发现,JMLUnitng对于引用类的测试是用null进行测试的;诸如-2147483648,0和2147483647之类,是测试边界情况,强度并不大,且缺乏随机性。
单元作业
第九次作业
第十次作业
第十一次作业
- UML类图
第十一次作业需要自己实现两个方法,一个是queryMinPath(),求两点之间的最短路径,一个是queryStrongLinked(),判断两点之间是否强连通。
求最短路径,我采用了dijkstra算法;
求是否强连通,我先找出两点之间所有的路径构成的图,然后删去任意一条边,判断此图是否强连通,如果存在一种强连通的情况,那么两点之间就是强连通的。
嗯……这次强测也很好的tle了:)
心得体会
一开始的JML的确是很好理解的,大部分都是根据注释填补代码的情况,只需要严格按照规格实现就OK,不过,慢慢的,尤其是第十一次作业里的queryMinPath()方法求两点间最短路径与queryStrongLinked()方法求两点之间是否强连通,JML只是给定了条件限制,具体实现完全靠自己发挥,JML的作用更多在于方便测试。
嗯还有一个单元,加油!