在开发和测试过程中,经常碰见需要构造虚假数据进行测试的情况。这是很常见的场景,很多编程语言都有库提供虚假数据构造:
开源库 datafaker 是 Java 相关虚假数据构造库,它是 java-faker 的替代产品,基于 Java 8 构建,具有最新的库和几个新添加的 Fake Generators。
官方地址: https://www.datafaker.net/
github 地址: https://github.com/datafaker-net/datafaker
pom 依赖:
<dependency>
<groupId>net.datafakergroupId>
<artifactId>datafakerartifactId>
<version>1.6.0version>
dependency>
datafaker 支持多种语言,默认英文,中文相关需要设置 Locale, 如下所示。
Faker faker = new Faker(Locale.CHINA);
Name name = faker.name();
logger.info("{}", name.name());
PhoneNumber phoneNumber = faker.phoneNumber();
logger.info("{}", phoneNumber.cellPhone());
Address address = faker.address();
logger.info("{}, {}, {}", address.state(), address.city(), address.streetAddress());
DateAndTime date = faker.date();
logger.info("{}, {}", date.birthday("yyyy年MM月dd日HH时mm分ss秒"), date.between(Timestamp.valueOf("2000-01-01 00:00:00"), Timestamp.valueOf("2099-12-31 23:59:59"))); // 1989年11月02日09时15分42秒, 2048-10-17 21:30:01.383
University university = faker.university();
logger.info("{}", university.name());
datafaker 提供了非常多的生成器,具体可以见:https://www.datafaker.net/documentation/providers/,但是除了上述部分,都是英文相关的,例如:电影,天气,书籍等等。其实也可以通过配置 yml 文件将电影、天气、书籍等中文数据参与构造假数据,通过看源码发现这些假数据都是通过配置文件配置,然后随机选择的,只不过生成器关于中文的比较少。具体操作可以查看下面 高级用法 自定义。
// 随机生成3-5个姓名
List<String> names =
faker.collection(() -> faker.name().name())
.len(3, 5)
.generate();
logger.info("{}", names); // [龚思源, 彭弘文, 邱睿渊]
// 随机生成10个身份证号码,其中 30% 为null
List<String> ids = faker.collection(() -> faker.idNumber().validZhCNSsn()).len(10).nullRate(0.3).generate();
logger.info("{}", ids);
硬编码数据
public static class Movie extends AbstractProvider {
private static final String[] MOVIE_NAMES = new String[]{ "肖申克的救赎", "霸王别姬", "阿甘正传", "泰坦尼克号",
"这个杀手不太冷", "美丽人生", "千与千寻", "辛德勒的名单", "盗梦空间", "忠犬八公的故事" };
public Movie(Faker faker) {
super(faker);
}
public String movie() {
return MOVIE_NAMES[faker.random().nextInt(MOVIE_NAMES.length)];
}
}
public static class MyCustomFaker extends Faker {
public MyCustomFaker(Locale locale) {
super(locale);
}
public Movie myMovie() {
return getProvider(Movie.class, () -> new Movie(this));
}
}
@Test
public void testFaker() {
MyCustomFaker customFaker = new MyCustomFaker(Locale.CHINA);
Movie movie = customFaker.myMovie();
logger.info("{}", movie.movie());
}
配置文件配置数据
# coding: utf-8
zh-CN:
faker:
books:
names: ['代码整洁之道', 'Java 编程思想', 'Java 编程从入门到入土']
authors: ['马丁(Robert C. Martin)', 'Bruce Eckel', 'aabond']
public static class MyCustomFaker extends Faker {
public MyCustomFaker(Locale locale) {
super(locale);
}
public Movie myMovie() {
return getProvider(Movie.class, () -> new Movie(this));
}
public BookFromFile bookFromFile() {
return getProvider(BookFromFile.class, () -> new BookFromFile(this));
}
}
public static class BookFromFile extends AbstractProvider {
private static final String KEY = "books";
public BookFromFile(Faker faker) {
super(faker);
faker.addPath(Locale.CHINA, Paths.get("src/test/java/books.yml"));
}
public String names() {
return faker.resolve(KEY + ".names");
}
public String authors() {
return faker.resolve(KEY + ".authors");
}
}
@Test
public void testFaker() {
MyCustomFaker customFaker = new MyCustomFaker(Locale.CHINA);
BookFromFile bookFromFile = customFaker.bookFromFile();
logger.info("{}, {}", bookFromFile.names(), bookFromFile.authors());
}
String e1 = faker.expression("#{letterify 'test????test'}"); // 将?替换为随机字母
String e2 = faker.expression("#{numerify '#test#'}"); // 将#替换为随机数字
String e3 = faker.expression("#{bothify '?#?#?#?#'}"); // 上述组合
String e4 = faker.expression("#{Name.last_name}#{Name.first_name}");// 能够调用其他Provider类
String e5 = faker.expression("#{templatify 'test','t','q','@'}"); // 能够将字符替换
logger.info("{}, {}, {}, {}, {}", e1, e2, e3, e4, e5);
// testzvkgtest, 6test3, r3t4f3x5, 宋梓晨, qes@
String json = Format.toJson(
faker.collection(faker::name)
.len(2)
.build())
.set("firstName", Name::firstName)
.set("lastName", Name::lastName)
.build()
.generate();
logger.info("{}", json); // [{"firstName": "绍齐", "lastName": "龙"},{"firstName": "聪健", "lastName": "韦"}]