前言
想要生成一些有些随机性的Javabean对象用来测试,又想让他们看上去像是真的?
想要一些大量的Javabean用来做测试,但是手动编写太累了?
想要用一套模板来定义一个Javabean的字段随机规则?
啥也不想,就进来看看?
如果你有以上想法或者需求,你可以试试一个叫做 Mock.java
的轻量级假数据生成框架,或者说工具。
github地址:https://github.com/ForteScarlet/Mock.java
gitee地址:https://gitee.com/ForteScarlet/Mock.java
废话不多说,下面我会先展示一个例子,然后再来做对这个项目介绍。
- 0 创建项目
我是使用 IntelliJ IDEA
写代码的,但是不管用什么,首先你需要创建一个maven项目,或者前去maven平台下载 Mock.java
的jar包,以下我会使用 maven
项目进行举例。
如果你选择下载jar包需要注意其内部依赖了
commons-beanutils-1.9.3
导入座标:
<!-- https://mvnrepository.com/artifact/io.gitee.ForteScarlet/mock.java -->
<dependency>
<groupId>io.gitee.ForteScarlet</groupId>
<artifactId>mock.java</artifactId>
<version>1.5.2</version>
</dependency>
然后等待依赖下载完成,创建一个有main
方法的类准备进行测试。
- 0001 写个User
毕竟是假数据,你需要一个载体。这里我写一个简单的 User
类作为测试用载体。记得重写toString
方法哦!方便测试。
public class User {
private String name;
private Integer age;
private String email;
private String password;
// 以下省略掉 getter、 setter、 toString
}
先写一下看看效果。
public static void main(String[] args) {
System.out.println(new User());
}
执行结果:
User{name='null', age=null, email='null', password='null'}
好,一切正常。那么接下来,我想要:
name
是一个随机的中文名称。age
是一个18-80之间的随机数。email
是一个随机的163邮箱。password
是一个6-16位数的随机字符。
那么该怎么办呢?接着往下看吧~
- 0010 构建模板并使用
Mock.java
中,生成一个“假对象”需要一个模板,然后通过解析这个模板中的参数以得知规则。
那么根据我上面提到的规则,模板如下:
模板的载体是一个Map<String, Object>,且是可嵌套的。
// 准备模板载体
Map<String, Object> template = new HashMap<>(2);
//name是一个随机的中文名称。
template.put("name", "@cname");
//age 是一个18-80之间的随机数。
template.put("age|18-80", 0);
//email是一个随机的163邮箱。
template.put("email", "@email(163, com)");
//password是一个6-16位数的随机字符。
template.put("password", "word(6,16)");
以上就是一个模板了。嗯?都是什么意思?先别着急,等我把这个例子讲完
那么模板怎么用呢?使用类Mock
的静态方法即可:
1.x版本中,Mock类的全路径:com.forte.util.Mock
// 准备模板载体
Map<String, Object> template = new HashMap<>();
//name是一个随机的中文名称。
template.put("name", "@cname");
//age 是一个18-80之间的随机数。
template.put("age|18-80", 0);
//email是一个随机的163邮箱。
template.put("email", "@email(163,com)");
//password是一个6-16位数的随机字符。
template.put("password", "@word(6,16)");
// 设置
Mock.set(User.class, template);
// 获取一个MockObject
MockObject<User> mockUser = Mock.get(User.class);
// 拿到一个假对象
User user = mockUser.getOne();
// 打印展示
System.out.println(user);
执行结果:
User{name='鲜于萍', age=38, email='[email protected]', password='gyqjkjo'}
我们可以看到,成功打印出来了有着数据的 User
对象。
那我要是想要很多个User怎么办呢?MockObject
对象中提供了丰富的方法以供使用,下面我就举几个例子:
// ...前置步骤省略
// 获取一个MockObject
MockObject<User> mockUser = Mock.get(User.class);
// 拿到20个user的list
List<User> list = mockUser.getList(20);
// 拿到20个user的list,并转化为它们的toString字符串
Set<String> set = mockUser.getSet(20, User::toString);
// 获取一个生成User对象的无限流
Stream<User> stream = mockUser.getStream();
// 通过并行流(即多线程)的方式获取20个user对象
List<User> listParallel = mockUser.getListParallel(20);
// ... 还有很多
由此可见,MockObject
提供了很多的方法用来对结果进行获取、聚合,拥有很多 Java8
中Stream
流的特点,也支持各种 lambda
表达式,很是方便。
这时候又有问题来了,假如我没有User
对象,但是我还是想要假数据,并且想把这些字段作为一个Map
的键保存怎么办?很简单,在上述示例的基础上修改一下:
// 准备模板载体
Map<String, Object> template = new HashMap<>();
//name是一个随机的中文名称。
template.put("name", "@cname");
//age 是一个18-80之间的随机数。
template.put("age|18-80", 0);
//email是一个随机的163邮箱。
template.put("email", "@email(163,com)");
//password是一个6-16位数的随机字符。
template.put("password", "@word(6,16)");
// 设置
Mock.set("user", template);
// 获取一个MockObject
MockObject<Map> mockUser = Mock.get("user");
Map userdata = mockUser.getOne();
// 展示数据
System.out.println(userdata);
// 查看各个value指的class类型
userdata.forEach((k, v) -> {
System.out.println(k + "=" + v + "\t("+ v.getClass() +")");
});
输出结果如下:
{name=逯丰者, password=mropdfensmp, age=32, [email protected]}
name=逯丰者 (class java.lang.String)
password=mropdfensmp (class java.lang.String)
age=32 (class java.lang.Integer)
[email protected] (class java.lang.String)
我们可以看到,只要把 Mock.set(Class, Map)
换成 Mock.set(String, Map)
就好啦!这样在用Mock.get
的时候就用填入的字符串获取,就会得到一个类型为 Map
的 MockObject
,而这个Map中所存放的就是最终的值。而且这个 value
值的类型一般也是根据最终值决定的,比如上文中的age
字段就是Integer
类型。
- 0011 区间参数
详细的介绍还请直接去 GITHUB 了解,GITHUB 的README文档是中文的。
上面介绍完了基础用法,但是大部分人看到这里应该还是不明白上面那些@cname
、age|18-80
的具体规则,尽管能够猜到它们是什么意思。
那接下来,我会简单的介绍一下规则。
首先先看看作为模板载体的 Map
中的 key
值。key
值称为(自称) 字段映射
,顾名思义就是用来定义其所指向的字段的。一个字段映射的格式如下:
field_name[|p1[-p2][.p3[,p4]]]
看上去有点乱哈,简单来说,中括号[]
中标注的内容为可选参数,同时被称为(自称) 区间参数
。
其中p1
、p2
为整数位参数,p3
,p4
为小数位参数。
如果有参数,则参数与字段之间用竖线|
分割,同位数参数用短横线-
分割(也就是减号),不同位数参数用点.
分割,且小数位在后,整数位在前。
举几个例子吧:
name
- 无参数
age|18
- 只有一个整数位参数
age|18-80
- 有两个整数位参数
money|100-200.0-2
- 有两个整数位参数和两个小数位参数
money|100-200.2
- 有两个整数位参数与一个小数位参数
- 0100 @函数
详细的介绍还请直接去 GITHUB 了解,GITHUB 的README文档是中文的。
对,没错,就叫“艾特函数”。顾名思义,就是开头带着一个@
的函数。@函数
一般会用在Map
模板的value
值中,例如:
// 准备模板载体
Map<String, Object> template = new HashMap<>();
//name是一个随机的中文名称。
template.put("name", "@cname");
上面这个@cname
就是一个@函数
,代表获取一个随机的中文姓名并赋值给name
字段。
那么这种@函数
一共都有那些呢?根据原作者的话说,所有的@函数
都是在初始化的时候动态读取MockUtil
这个类的静态方法而得到的,所以如果想知道有哪些@函数
可以用,直接进入到MockUtil
这个类中看看就可以了。
值得一提的是,@函数
可以存在参数,也可以存在多个。当有多个可解析的@函数
的时候,其最终结果会被尝试使用+
运算进行计算。不过目前还不支持@函数
的嵌套。
// 准备模板载体
Map<String, Object> template = new HashMap<>();
//email是一个随机的163邮箱。
template.put("email", "@email(163,com)");
// name = 中文名 + 2-20之间的随机值
template.put("name", "@cname@integer(2,20)");
上面所得到的结果示例如下:
[email protected]
name=茅吾表10
不过需要注意的是,@函数的多个参数之间直接用逗号
,
分隔且不能存在空格。
- 0101 翻翻源代码
大概的使用方式已经知道了,那么它的实现原理到底是什么呢?不过其实不用说大家也大概都能猜到了,就是通过反射获取字段,然后解析定义的那些规则,再进行封装,最终通过反射获取你想要的JavaBean,并对它们赋值,然后提供给使用者。
其内部定义了不少针对不同的value
参数的解析器用来应对各种各样的情况,而作者也很“贴心”的在github中为整个框架中一些比较核心的类或者接口取了个名字并做了简单的介绍。获取没有用到什么比较高深的东西,甚至有可能会有一些比较低级的代码,但是它依旧可以作为一个入门级的小项目用来研究与学习。
总而言之就是反射,没有什么大道理。
- 0110 有点眼熟?
有些人可能在看到这个age|1-2.0-1
的时候就觉得有点眼熟:这不是前端的假数据测试库mock.js
的语法么?然后再看看这个项目名:Mock.java
,嗯?有一种恍然大悟的感觉。
没错,作者的灵感就是来源于Mock.js
,并且这种区间函数
和@函数
的语法格式也是借鉴了Mock.js
来的,这些在他github项目的简介处和文档处也是有做说明的。
- 0110 结尾
大概就是这样,只是做一些简单的介绍,而一些具体的规则、逻辑,可能还是要去看看官方的github文档或者自己手动尝试一下。同时希望在看github的时候,能够作者点亮一颗star
,以示鼓励 😃
嗯?你问我为什么要帮作者讨要star?因为我就是作者丫~ 😄
新手写文章,如有不妥请谅解~