背景

最近在重构一个框架的代码,在重构的过程中就在想一个问题,如何做单元测试,当然,这个问题不是如何使用JUnit的问题,而且如何使做单元测试更充分,如果你有时间写几千个测试用例,这个也许不是问题,但当你的系统足够庞大,而你的人力和时间有限的情况下,如何最大限度的完成单元测试。

天上是不会掉馅饼的,有的话也只会砸到天才,砸不到普通人,我们都是普通人,不是天才,据说天才们已经想到方案了睡觉的时候,程序能不能自动查 bug,让你在睡觉的时候自动帮你查bug。如果没有完美的方案,有没有退而求其次的方案。这个就是今天要讨论的主题。

单元测试主要解决两个问题

  • 系统错误
  • 业务错误


系统错误是指,你的代码会不会报500,业务错误是指程序处理的结果符不符合预期,在单元测试中,系统错误由单元测试框架自动处理,框架会抛出exception,业务错误由开发人员指定,通过Assert方式实现。

我不指望不写一行代码就能解决我业务错误,但我觉得可以通过自动化但方式解决系统错误。实际上,如果我们的系统能够做到没有系统错误,已经是很健壮了。

实现

迷宫

如果把我们的代码比作一座迷宫,每个函数都是迷宫中的一个通道,迷宫由入口和出口,那么只要我们在迷宫中逛的时间足够长,走过的路径足够多,就能把整个迷宫都走一遍。用到单元测试上,就是只要单元测试程序在你的代码里长时间的瞎转悠,就能把你代码都走过一遍,也就是我们所说的代码覆盖率,随机参数+随机路径,只要这两个随机样本足够大,理论上就能达到100%的代码覆盖率并且完成各种场景参数测试。而这个刚好是自动化最擅长的,因此理论上是可以实现的。

脚本

如果我们希望实现以上构想,我们需要一个能够描述单元测试的语言,至今还未见过专门的单元测试语言,你可以用json,但json可读性和书写都很麻烦,我一直觉得markdown是最好的标记格式,我们可以用markdown描述我们的单元测试信息,程序通过读取描述文件进行自动化单元测试,我大概列了一个描述文件的样例。

# [config]
<li>casecount:100</li>
<li>level:10</li>
# [object]MpaasUser
# [class]MpaasQuery
<h2>eq</h2>
<li>position:first</li>
<li>rand:0%</li>
<li>times:1-100</li>



field
  • type:string
  • value:rand/static
  • valueType:raw/normal

不仅程序容易处理,可读性也很强,在markdown编辑器下效果是这样的。

demo

写了一个demo,运行的结果如下:

public void case1_mpaasquery{
        query.conjuctionOr()
                .distinct("string")
                .ne("string", "string")
                .view("string")
                .orderBy("string", "string")
                .include("string")
                .in("string", "string")
                .conjuctionOr()
                .unSelect("string")
                .exclude("string")
                .table("string")
                .groupBegin()
                .ne("string", "string")
                .in("string", "string")
                .notIn("string", "string")
                .exclude("string")
                .distinct("string")
                .rowid("string")
                .gt("string", "string")
                .update("string")
                .addClause("string", "string", "string")
                .eqNocase("string", "string")
                .clone()
                .update("string")
                .in("string", "string")
                .isNull("string")
                .distinct("string")
                .isNull("string")
                .orderBy("string", "string")
                .update("string")
                .setVar("string", "string")
                .pageBegin(100)
                .addRowIdClause("string", "string", "string")
                .notLike("string", "string")
                .notIn("string", "string")
                .rowid("string", "string", "string")
                .sql("string")
                .hashCode()
                .isNull("string")
                .groupBegin()
                .countSql("string")
                .eq("string", "string")
                .eqNocase("string", "string")
                .isNull("string")
                .and()
                .notLike("string", "string")
                .isNull("string")
                .rowid("string")
                .and()
                .update("string")
                .likeNocase("string", "string")
                .select("string")
                .sql("string")
                .countSql("string")
                .groupBy("string")
                .conjuctionAnd()
                .like("string", "string")
                .eqNocase("string", "string")
                .lt("string", "string")
                .page(100)
                .table("string")
                .groupBegin()
                .countSql("string")
                .countSql("string")
                .orderBy("string", "string")
                .setVar("string", "string")
                .ne("string", "string")
                .sql("string")
                .exclude("string")
                .notifyAll()
                .eq("string", "string")
                .update("string", "string")
                .groupBy("string")
                .select("string")
                .gteq("string", "string")
                .isNull("string")
                .lt("string", "string")
                .pageSize(100)
                .like("string", "string")
                .gt("string", "string")
                .getClass()
                .isNotNull("string")
                .setVar("string", "string")
                .conjuctionOr()
                .sql("string")
                .update("string")
                .bind("string")
                .notifyAll()
                .rowid("string", "string", "string")
                .notifyAll()
                .or()
                .ne("string", "string")
                .update("string")
                .toString()
                .notNull("string")
                .table("string")
                .page(100)
                .doPageQuery(100, 100, "string");
    }

程序自动生成测试的脚本,你可以指定level控制深度,上面这个demo在语法上是没问题的,已经达到想要的效果,下一步就是控制好参数,让函数里填进正确的参数。

Trackback

no comment untill now

Add your comment now