Generates realistically-looking fake data
Just like this fake-logo, but not quite so.
- About
- Comparison with other JVM-based faker libraries
- Usage
- CLI
- Data Providers
- Migrating to 1.0
- For developers
- Build and Deploy
- Contributing
- Thanks
- Licence
Port of a popular ruby faker gem written in kotlin. Generates realistically looking fake data such as names, addresses, banking details, and many more, that can be used for testing purposes during development and testing.
While there are several other libraries out there with similar functionalities, I had several reasons for creating this one:
- most of the ones I've found are written in java and I wanted to use kotlin
- none of them had the functionality I needed
- I didn't feel like forking an existing kotlin-based lib - fakeit - and refactoring the entire codebase, especially with it not being maintained for the past couple of years.
So why use this one instead? I've decided to make a comparison between kotlin-faker, and others libs that have been out there for quite some time.
The benchmarks time is an average execution time of 10 consecutive runs. Each run includes creating a new Faker instance and generating a 1_000_000 values with the function returning a person's full name.
Note: benchmarks for blocoio/faker
could not be done due to unexpected exceptions coming from the lib, benchmarks for moove-it/fakeit
could not be done due to android dependencies in the lib
kotlin-faker | DiUS/java-faker | Devskiller/jfairy | blocoio/faker | moove-it/fakeit | |
---|---|---|---|---|---|
language | kotlin | java | java | java | kotlin |
number of available providers (address , name , etc.) |
171 | 73 | 8 | 21 | 36 |
number of available locales | 55 | 47 | 10 | 46 | 44 |
extra functionality (i.e. randomClassInstance gen) |
✅ | ❎ | ❎ | ❎ | ❎ |
actively maintained | ✅ | ✅ | ✅ | ✅ | ❎ |
cli-bot app | ✅ | ❎ | ❎ | ❎ | ❎ |
benchmarks | 5482ms | 17529.9ms | 15036.5ms | NA | NA |
Latest releases are always available on jcenter.
From v1.4.1
onward releases are also published on maven central.
With gradle
dependencies {
implementation 'io.github.serpro69:kotlin-faker:$version'
}
With maven
<dependencies>
<dependency>
<groupId>io.github.serpro69</groupId>
<artifactId>kotlin-faker</artifactId>
<version>${version}</version>
</dependency>
</dependencies>
Using release candidate versions
Release candidates contain the newest functionality before next version gets released and can be downloaded by adding the following repo:
Gradle:
repositories {
maven {
url 'https://dl.bintray.com/serpro69/maven-release-candidates/'
}
}
Maven:
<repositories>
<repository>
<id>serpro69-maven</id>
<url>https://dl.bintray.com/serpro69/maven-release-candidates/</url>
<layout>default</layout>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
Downloading a jar
The jar and pom files can also be found at
this link
val faker = Faker()
faker.name.firstName() // => Ana
faker.address.city() // => New York
If no FakerConfig
instance is passed to Faker
constructor then default configuration will be used:
locale
is set toen
random
is seeded with a pseudo-randomly generated number.uniqueGeneratorRetryLimit
is set to100
Faker supports seeding of it's PRNG (pseudo-random number generator) through configuration to provide deterministic output of repeated function invocations.
val fakerConfig = FakerConfig.builder().create {
random = Random(42)
}
val faker = Faker(fakerConfig)
val city1 = faker.address.city()
val name1 = faker.name.name()
val otherFaker = Faker(fakerConfig)
val city1 = otherFaker.address.city()
val name1 = otherFaker.name.name()
city1 == city2 // => true
name1 == name2 // => true
Faker supports generation of unique values. There are basically two ways to generate unique values:
Unique values for entire provider
val faker = Faker()
faker.unique.configure {
enable(faker::address) // enable generation of unique values for address provider
}
// will generate unique country each time it's called
repeat(10) { faker.address.country() }
To clear the record of unique values that were already generated:
faker.unique.clear(faker::address) // clears used values for address provider
faker.unique.clearAll() // clears used values for all providers
To disable generation of unique values:
faker.unique.disable(faker::address) // disables generation of unique values for address provider and clears all used values
faker.unique.disableAll() // disables generation of unique values for all providers and clears all used values
Unique values for particular functions of a provider
val faker = Faker()
repeat(10) { faker.address.unique.country() } // will generate unique country each time `country()` is prefixed with `unique`
repeat(10) { faker.address.city() } // this will not necessarily be unique (unless `faker.unique.enable(faker::address)` was called previously)
To clear the record of unique values that were already generated:
faker.address.unique.clear("city") // clears used values for `faker.address.unique.city()` function
faker.address.unique.clearAll() // clears used values for all functions of address provider
Configuring retry limit
If the retry count of unique generator exceeds the configured value (defaults to 100
)
then RetryLimitException
will be thrown.
It is possible to re-configure the default value through FakerConfig
:
val config = FakerConfig.builder().create {
uniqueGeneratorRetryLimit = 1000
}
val faker = Faker(config)
Excluding values from generation
It is possible to exclude values from being generated with unique generator. This is configured on the faker
level for
each (or in some cases - all) of the providers.
val faker = Faker()
faker.unique.configuration {
// Enable generation of unique values for Address provider
// Any unique generation configuration will only affect "enabled" providers
enable(faker::address)
// Exclude listOfValues from being generated
// in all providers that are enabled for unique generation
exclude(listOfValues)
// Exclude values starting with "A" from being generated
// in all providers that are enabled for unique generation
exclude { listOf(Regex("^A")) }
// Additional configuration for particular provider
// First enable generation of unique values for Name provider
enable(faker::name) {
// Exclude listOfNames from being generated by any Name provider function
excludeFromProvider<Name>(listOfNames)
// Exclude listOfLastNames from being generated by Name#lastName function
excludeFromFunction(Name::lastName, listOfLastNames)
// Exclude values starting with "B" from being generated by any Name provider function
excludeFromProvider<Name> { listOf(Regex("^B")) }
// Exclude values starting with "C" from being generated by Name#country function
excludeFromFunction(Name::lastName) { listOf(Regex("^C")) }
}
}
// Based on the above config the following will be true in addition to generating unique values:
val city = faker.address.city()
assertTrue(listOfValues.contains(city) == false)
assertTrue(city.startsWith("A") == false)
val firstName = faker.name.firstName()
val lastName = faker.name.lastName()
assertTrue(listOfValues.contains(firstName) == false)
assertTrue(listOfValues.contains(lastName) == false)
assertTrue(listOfNames.contains(firstName) == false)
assertTrue(listOfNames.contains(lastName) == false)
assertTrue(listOfLastNames.contains(lastName) == false)
assertTrue(firstName.startsWith("A") == false)
assertTrue(lastName.startsWith("A") == false)
assertTrue(firstName.startsWith("B") == false)
assertTrue(lastName.startsWith("B") == false)
assertTrue(lastName.startsWith("C") == false)
This is only applicable when the whole category, i.e. Address
or Name
is enabled for unique generation of values.
faker.address.unique.country() // will still generate unique values, but won't consider exclusions, if any
Faker
can be configured to use a localized dictionary file instead of the default en
locale.
val fakerConfig = FakerConfig.builder().create {
locale = "nb-NO"
}
val faker = Faker(fakerConfig)
val city1 = faker.address.city() // => Oslo
List of available locales (clickable):
ar
bg
ca
ca-CAT
da-DK
de
de-AT
de-CH
ee
en
- defaulten-AU
en-au-ocker
en-BORK
en-CA
en-GB
en-IND
en-MS
en-NEP
en-NG
en-NZ
en-PAK
en-SG
en-TH
en-UG
en-US
en-ZA
es
es-MX
fa
fi-FI
fr
fr-CA
fr-CH
he
hy
id
it
ja
ko
lv
nb-NO
nl
no
pl
pt
pt-BR
ru
sk
sv
th
tr
uk
vi
zh-CN
zh-TW
Using a non-default locale will replace the values in some of the providers with the values from localized dictionary.
val fakerConfig = FakerConfig.builder().create {
locale = "es"
}
val faker = Faker(fakerConfig)
faker.address.city() // => Barcelona
Note that if the localized dictionary file does not contain a category (or a parameter in a category) that is present in the default locale, then non-localized value will be used instead.
val faker = Faker()
faker.gameOfThrones.cities() // => Braavos
val fakerConfig = FakerConfig.builder().create {
locale = "nb-NO"
}
val localizedFaker = Faker(fakerConfig)
// `game_of_thrones` category is not localized for `nb-NO` locale
localizedFaker.gameOfThrones.cities() // => Braavos
Although this lib was created with Kotlin in mind it is still possible to use from a Java-based project thanks to great Kotlin-to-Java interop.
Configuring Faker
:
FakerConfig fakerConfig=FakerConfigBuilder.create(FakerConfig.builder(),fromConsumer(builder->{
builder.setRandom(new Random(42));
builder.setLocale("en-AU");
}));
If builder
parameter is not called with help of fromConsumer
method, then explicit return should be specified:
FakerConfig fakerConfig=FakerConfigBuilder.create(FakerConfig.builder(),builder->{
builder.setRandom(new Random(42));
builder.setLocale("en-AU");
return Unit.INSTANCE;
});
Calling Faker
methods:
new Faker(fakerConfig).getName().firstName(); // => John
Command line application can be used for a quick lookup of faker functions. See faker-bot README for installation and usage details.
Below is the list of available providers that correspond to the dictionary files found in core/locales/en
Note that not all (although most) of the providers and their functions are implemented at this point. For more
details see the particular .md
file for each provider below.
List of available providers (clickable):
- Address
- Ancient
- Animal
- App
- Appliance
- AquaTeenHungerForce
- Artist
- BackToTheFuture
- Bank
- Barcode
- Basketball
- Beer
- BigBangTheory
- Blood
- BojackHorseman
- Book
- BossaNova
- BreakingBad
- Buffy
- Business
- Cannabis
- Cat
- Chiquito
- ChuckNorris
- Code
- Coffee
- Coin
- Color
- Commerce
- Community
- Company
- Compass
- Computer
- Construction
- Control
- Cosmere
- CryptoCoin
- CultureSeries
- Currency
- CurrencySymbol
- DcComics
- Demographic
- Departed
- Dessert
- Device
- DnD
- Dog
- Dota
- DragonBall
- DrivingLicense
- Drone
- DrWho
- DumbAndDumber
- Dune
- Educator
- ElderScrolls
- ElectricalComponents
- ESport
- Fallout
- FamilyGuy
- File
- Finance
- Food
- Football
- FreshPriceOfBelAir
- Friends
- FunnyName
- Futurama
- Game
- GameOfThrones
- Gender
- GhostBusters
- GratefulDead
- GreekPhilosophers
- Hacker
- HalfLife
- HarryPotter
- Heroes
- HeroesOfTheStorm
- HeyArnold
- Hipster
- HitchhikersGuideToTheGalaxy
- Hobbit
- Horse
- House
- HowIMetYourMother
- IdNumber
- IndustrySegments
- Internet
- Invoice
- Job
- KPop
- LeagueOfLegends
- Lebowski
- LordOfTheRings
- Lorem
- Lovecraft
- Markdown
- Marketing
- Measurement
- MichaelScott
- Military
- Minecraft
- Money
- Movie
- Music
- Myst
- Name
- Nation
- NatoPhoneticAlphabet
- NewGirl
- OnePiece
- Opera
- Overwatch
- ParksAndRec
- PearlJam
- Phish
- PhoneNumber
- Pokemon
- Prince
- PrincessBride
- ProgrammingLanguage
- Quote
- Rajnikanth
- RandomProvider
- Relationship
- Restaurant
- RickAndMorty
- RockBand
- Rupaul
- Rush
- Science
- Seinfeld
- Separator
- Shakespeare
- Show
- SiliconValley
- Simpsons
- SlackEmoji
- SonicTheHedgehog
- Source
- SouthPark
- Space
- Stargate
- StarTrek
- StarWars
- StrangerThings
- StreetFighter
- Stripe
- Subscription
- [Suits](doc/Suits.md
- Superhero
- SuperSmashBros
- SwordArtOnline
- Team
- TheExpanse
- TheITCrowd
- TheThickOfIt
- TwinPeaks
- UmphreysMcgee
- University
- Vehicle
- VentureBros
- Verbs
- VForVendetta
- WarhammerFantasy
- Witcher
- WorldCup
- WorldOfWarcraft
- Yoda
- Zelda
There are some rules when creating a random instance of a class:
- The constructor with the least number of arguments is used (This can be configured - read on.)
kotlin.collection.*
andkolin.Array
types in the constructor are not supported at the moment
To generate a random instance of any class use Faker().randomProvider
. For example:
class Foo(val a: String)
class Bar(val foo: Foo)
class Test {
@Test
fun test() {
val faker = Faker()
val foo: Foo = faker.randomProvider.randomClassInstance()
val bar: Bar = faker.randomProvider.randomClassInstance()
}
}
Some, or all, of the constructor params can be instantiated with values following some pre-configured logic using typeGenerator
function. Consider the following example:
class Baz(val id: Int, val uuid: UUID)
class Test {
@Test
fun test() {
val faker = Faker()
val baz: Baz = faker.randomProvider.randomClassInstance {
typeGenerator<UUID> { UUID.fromString("00000000-0000-0000-0000-000000000000") }
typeGenerator<Int> { 0 }
}
}
}
For each instance of Baz
the following will be true:
baz.id == 0
baz.uuid == UUID.fromString("00000000-0000-0000-0000-000000000000")
The example itself does not make that much sense, since we're using "static" values, but we could also do something like:
val baz: Baz = faker.randomProvider.randomClassInstance {
typeGenerator<UUID> { UUID.randomUUID() }
}
or even:
class Person(val id: Int, val name: String)
class Test {
@Test
fun test() {
val faker = Faker()
val person: Person = faker.randomProvider.randomClassInstance {
typeGenerator<String> { faker.name.fullName() }
}
}
}
By default, the constructor with the least number of args is used when creating a random instance of the class. This might not always be desirable and can be configured. Consider the following data classes:
class Foo
class Bar(val int: Int)
class Baz(val foo: Foo, val string: String)
class FooBarBaz {
var foo: Foo? = null
private set
var bar: Bar? = null
private set
var baz: Baz? = null
private set
constructor(foo: Foo) {
this.foo = foo
}
constructor(foo: Foo, bar: Bar) : this(foo) {
this.bar = bar
}
constructor(foo: Foo, bar: Bar, baz: Baz) : this(foo, bar) {
this.baz = baz
}
}
If there is a need to use the constructor with 3 arguments when creating an instance of FooBarBaz
, we can do it like so:
class Test {
@Test
fun test() {
val faker = Faker()
val fooBarBaz: FooBarBaz = randomProvider.randomClassInstance {
constructorParamSize = 3
fallbackStrategy = FallbackStrategy.USE_MAX_NUM_OF_ARGS
}
}
}
In the above example, FooBarBaz
will be instantiated with the first discovered constructor that has parameters.size == 3
; if there are multiple constructors that satisfy this condition - a random one will be used. Failing that (for example, if there is no such constructor), a constructor with the maximum number of arguments will be used to create an instance of the class.
Alternatively to constructorParamSize
a constructorFilterStrategy
config property can be used as well:
class Test {
@Test
fun test() {
val faker = Faker()
val fooBarBaz: FooBarBaz = randomProvider.randomClassInstance {
constructorFilterStrategy = ConstructorFilterStrategy.MAX_NUM_OF_ARGS
}
}
}
There are some rules to the above:
constructorParamSize
config property takes precedence overconstructorFilterStrategy
- both can be specified at the same time, though in most cases it probably makes more sense to use
failbackStrategy
withconstructorParamSize
as it just makes things a bit more readable - configuration properties that are set in
randomClassInstance
block will be applied to all "children" classes. For example classesFoo
,Bar
, andBaz
will use the same random instance configuration settings when instances of those classes are created inFooBarBaz
class.
Prior to version 1.0:
Faker
was a singleton.- Random seed was provided through
Faker.Config
instance. - Locale was provided as parameter to
init()
function. - Provider functions were function literals. If
invoke()
was explicitly specified, then it will have to be removed ( See below.)
After version 1.0:
Faker
is a class.- Configuration (rng, locale) is set with
FakerConfig
class. An instance ofFakerConfig
can be passed toFaker
constructor. - Provider functions are no longer function literals. Explicit calls to
invoke()
will throw compilation error.
- // prior to version 1.0
- Faker.Config.random = Random(42)
- Faker.init(locale)
- Faker.address.city()
- // or with explicit `invoke()`
- Faker.address.country.invoke()
+ // since version 1.0
+ // locale and random configuration is set with `FakerConfig` class (See Usage in this readme)
+ val faker = Faker(fakerConfig)
+ faker.address.city()
+ // explicit calls to `invoke()` have to be removed
+ faker.address.country()
Apart from changes to configuring locale and random seed and instantiating Faker
through constructor instead of using
a singleton instance (see kotlin examples), the main difference for java users is that provider functions are no longer
function literals, therefore calls to invoke()
operator will have to be removed and getters replaced with function
calls.
- // prior to version 1.0
- Faker.init(locale);
- Faker.getAddress().getCity().invoke();
+ // since version 1.0
+ Faker faker = new Faker(fakerConfig);
+ // note `city()` function is called instead of getter
+ // and no call to `invoke()` operator
+ faker.getAddress().city();
When adding a new dictionary yml file the following places need to reflect changes:
- Dictionary.kt - add a new class
to
CategoryName
enum. This is only necessary if the category is not already there. - Constants.kt - the new dictionary filename should be
added to
defaultFileNames
property. - Faker.kt - add a new faker provider property to
Faker
class. - The provider implementation class should go into provider package.
- doc - add an
.md
file for the new provider. - reflect-config.json has to be updated to build the native image with graal.
- And of course unit tests.
Build/deploy to bintray and github release processes are automated with travis-ci through usage of git tags.
Versions need to be bumped manually through a tag with the next release version that has to follow the semver rules, and the tag has to be pushed to origin.
Creating the tag can be either done manually with git tag
or by using gradlew tag
task.
To create a new pre-release version (new release candidate)
the following can be used: ./gradlew clean tag -Prelease -PnewPreRelease -PbumpComponent={comp}
, where comp
can be
one of the following values: major
, minor
, or patch
.
To bump an existing pre-release to the next version (next release candidate for the same release version)
the following can be used: ./gradlew clean tag -Prelease -PpreRelease
.
To promote a pre-release to a release version the following can be used:
./gradlew clean tag -Prelease -PpromoteToRelease
,
To create a new release version the following can be used:
./gradlew clean tag -Prelease -PbumpComponent={comp}
, where comp
can be one of the following values: major
, minor
, or patch
.
Alternatively to the above targets from Makefile can be used for the same purposes.
Feel free to submit a pull request and/or open a new issue if you would like to contribute.
Many thanks to these awesome tools that help us in creating open-source software:
This code is free to use under the terms of the MIT licence. See LICENCE.md.