Faker (OCaml)
Generate massive amounts of fake (but realistic) data for testing and development.
An OCaml port of @faker-js/faker 10.6.0 with seed-for-seed output parity.

๐ Features
- ๐ง Person - Generate Names, Genders, Bios, Job titles, and more.
- ๐ Location - Generate Addresses, Zip Codes, Street Names, States, and Countries!
- โฐ Date - Past, present, future, recent, soon... whenever!
- ๐ธ Finance - Create stubbed out Account Details, Transactions, and Crypto Addresses.
- ๐ Commerce - Generate Prices, Product Names, Adjectives, and Descriptions.
- ๐พ Hacker - โTry to reboot the SQL bus, maybe it will bypass the virtual application!โ
- ๐ข Number and String - Of course, we can also generate random numbers and strings.
- ๐ Localization - All 77 faker-js locales, from
af_ZA to zu_ZA. - ๐ฏ Seed-for-seed parity - For the same seed, every method returns the same value faker-js returns. The port uses the same MersenneTwister19937 generator and the same algorithms, and
dune test checks this against fixtures generated by running faker-js itself. - ๐ชถ Lightweight - Depends only on the OCaml stdlib and
unix.
๐ฆ Install
opam install faker
For the development version, pin it from git:
opam pin add faker https://github.com/barteo/faker-ocaml.git
Then add it to your dune file:
(executable
(name main)
(libraries faker))
๐ช Usage
type user = {
user_id : string;
username : string;
email : string;
avatar : string;
password : string;
birthdate : float; (* epoch milliseconds *)
registered_at : float;
}
(* Bind each value with [let] so the RNG is consumed in the same order as faker-js
(OCaml evaluates record fields right to left). *)
let create_random_user f =
let user_id = Faker.String.uuid f in
let username = Faker.Internet.username f in
let email = Faker.Internet.email f in
let avatar = Faker.Image.avatar f in
let password = Faker.Internet.password f in
let birthdate = Faker.Date.birthdate f in
let registered_at = Faker.Date.past f in
{ user_id; username; email; avatar; password; birthdate; registered_at }
let () =
let f = Faker.create () in
let users = Faker.Helpers.multiple ~count:(`N 5) (fun _ -> create_random_user f) f in
Array.iter
(fun u -> Printf.printf "%s <%s>, born %s\n" u.username u.email (Faker.Date_util.to_iso u.birthdate))
users
A few more, with a fixed seed:
let () =
let f = Faker.create ~seed:42 () in
print_endline (Faker.Person.full_name f); (* Nikita Crist *)
print_endline (Faker.Location.street_address ~use_full_address:true f);
print_endline (Faker.Internet.email f);
print_endline (Faker.String.uuid f);
Printf.printf "%d\n" (Faker.Number.int ~min:1 ~max:10 f);
print_endline (Faker.Helpers.from_reg_exp "[A-Z]{3}-[0-9]{4}" f)
Conventions
The API mirrors faker-js, so the faker-js API reference documents every method. It translates like this:
faker-js | OCaml |
|---|
faker.person.firstName({ sex: 'female' })
| Faker.Person.first_name ~sex:Female f`
|
faker.number.int({ min: 1, max: 10 })
| Faker.Number.int ~min:1 ~max:10 f
|
faker.string.alpha({ length: { min: 3, max: 5 } })
| Faker.String.alpha ~length:(Range (3, 5)) f`
|
faker.number.bigInt({ max: 10n ** 30n })
| Faker.Number.big_int ~max:(Faker.Bigint.of_string "1000000000000000000000000000000") f
|
faker.helpers.arrayElement(arr)
| Faker.Helpers.array_element arr f
|
faker.helpers.enumValue(Color)
| Faker.Helpers.enum_value [ ("Red", "red"); ... ] f
|
new Faker({ locale: [en, base], seed })
| Faker.create ~seed ()
|
new Faker({ locale: [de_AT, de, en, base] })
| Faker.create ~locale:(Faker.Locales.De_AT.chain ()) ()
|
fakerDE
| Faker.Locales.De.faker ()
|
de
| Faker.Locales.De.definition ()
|
allFakers, allLocales
| Faker.All_locales.all_fakers, all_locales
|
new SimpleFaker({ seed })
| Faker.create_simple ~seed ()
|
faker.seed(42)
| Faker.seed f 42
|
faker.seed()
| Faker.seed_random f
|
faker.setDefaultRefDate(date)
| Faker.set_default_ref_date f ms, or Faker.set_default_ref_date_input f (`Str iso)
|
faker.setDefaultRefDate()
| Faker.reset_default_ref_date f
|
faker.getMetadata()
| Faker.get_metadata f
|
faker.rawDefinitions / faker.definitions.person.first_name
| Faker.definitions f / Faker.definition f "person" "first_name"
|
mergeLocales([de, en])
| Faker.merge_locales [ de; en ]
|
- The faker instance always comes last, and options are optional labelled arguments in snake_case.
- A
NumberOrRange option becomes `N n or `Range (min, max). String unions become polymorphic variants. - Dates are
float epoch milliseconds. Use Faker.Date_util.to_iso and of_iso to convert. - A
bigint is a Faker.Bigint.t, an arbitrary-precision integer (of_int, of_string, to_string, ...). - Errors raise
Faker.Faker_error msg, with the same messages as faker-js.
๐ Modules
All 26 faker-js modules are ported, each as Faker.<Module>: Airline, Animal, Book, Color, Commerce, Company, Database, Datatype, Date, Finance, Food, Git, Hacker, Helpers, Image, Internet, Location, Lorem, Music, Number, Person, Phone, Science, String, System, Vehicle and Word.
Templates
Faker.Helpers.fake combines faker methods using a mustache string format:
print_endline
(Faker.Helpers.fake "Hello {{person.prefix}} {{person.lastName}}, how are you today?" f)
๐ Localization
All 77 faker-js locales are included. Faker.create uses English (en with base as fallback). Every locale has a module in Faker.Locales, named after its code (De, De_AT, Pt_BR, Zh_CN, ...), with the same pieces faker-js exports:
let () =
let f = Faker.Locales.De.faker () in (* fakerDE: a shared instance *)
Faker.seed f 42;
print_endline (Faker.Person.full_name f); (* Melinda Dragu *)
(* Your own instance, with the same fallback chain as fakerDE_AT: de_AT, de, en, base. *)
let g = Faker.create ~locale:(Faker.Locales.De_AT.chain ()) ~seed:42 () in
print_endline (Faker.Location.city g)
Like importing a single locale from @faker-js/faker, a program only links the locales it references, so English-only programs stay small. Faker.All_locales (allLocales, allFakers) references every locale and links all of them (about 1.2 MB more).
Locale data is generated from the faker-js npm package with node tools/gen_locale.mjs, so it is never hand-copied (see CONTRIBUTING.md).
โ๏ธ Setting a randomness seed
If you want consistent results, you can set your own seed:
let () =
let f = Faker.create () in
Faker.seed f 123;
let first_random = Faker.Number.int f in
(* Setting the seed again resets the sequence. *)
Faker.seed f 123;
let second_random = Faker.Number.int f in
Printf.printf "%b\n" (first_random = second_random) (* true *)
Faker.Date methods are relative to the current time by default. For reproducible dates, also fix the reference date with Faker.set_default_ref_date f ms, or pass ~ref_date.
๐ Development
opam install dune alcotest
dune build
dune test # parity fixtures + property tests
ONLY=person dune test --force # one module's parity fixtures
ONLY='sweep_*' dune test --force # every method in every locale (or sweep_de, l10n_ja, ...)
dune exec ./bin/demo.exe -- 42 # one value per module
dune exec ./bin/demo.exe -- 42 ja # ... in another locale
Fixtures come from the real faker-js package (tools/package.json pins 10.6.0):
cd tools && npm install
node gen_locale.mjs # lib/locales/: every locale's data, chain and instance
node gen_unicode.mjs # lib/internal/unicode_*.ml: JS case mapping and NFKD tables
node gen_fixtures.mjs [group] # test/expected/expected_<group>.ml from tools/cases/<group>.mjs
See docs/PORTING.md for the porting conventions and the parity pitfalls, such as FMA contraction on arm64, UTF-16 vs UTF-8, and JS number formatting.
โ ๏ธ Known differences
- Date strings are parsed as ISO 8601. A date-time string without a timezone offset is read as UTC, while JS reads it as local time.
finance.amount ~auto_format:true always formats as en-US. faker-js uses the runtime's default locale (not the faker locale), so this matches node running in en-US.- Through
helpers.fake, a date-returning method yields an ISO string. JS would produce its locale-dependent Date.toString(). location.nearbyGPSCoordinate uses a port of V8's fdlibm sin/cos, which is bit-exact with node on arm64.Distributor.exponential calls the platform's libm pow, as node's Math.pow does. Its results can differ in the last bit between platforms (e.g. FreeBSD vs Linux/macOS) in faker-js too, so they match faker-js running on the same platform.
โจ Contributing
Please read the Contributing Guide and the Porting Guide before making a pull request.
๐ Credits
All the data and algorithms come from faker-js. Thanks to the faker-js team and everyone who contributed to it, and to Marak Squires for the original faker.js.
๐ License
MIT. It includes the faker-js license, since the locale data is derived from faker-js.