faker: An awesome library to give you dynamic test data with option to test multiple locales in your test

Pramod Yadav
5 min readFeb 13, 2021

Say “no more” to static test data in your tests!

This is how I used to write my tests before I got introduced to faker.

@Test
public void assertThatAPersonCanBeAddedByFillingOnlyMandatoryFields() {
person.setPersonalDetails("", "Yadav", "Pramod", "IN");
person.confirm();

assertWithPersonTable(sqlPerson, "", "Pramod", "Yadav", "IN");
}

Nothing wrong with it, you may say. I follow good design practices, specifically in context of above test:

  • I named a test that is readable
  • Clearly shows the intentions of tests
  • Have an abstraction layer in tests, that shows my “business intentions” rather than “page interactions”
  • Small atomic tests
  • Single assertions
  • All in all looks good;

Except the test data part.

Why? What's wrong you say?

Two things.

One, if your application is sensitive towards locale, these tests do not help providing data per locale.

Two, the data in tests is fixed which doesn’t help much addressing the problem of pesticide paradox; which states:

If the same tests are repeated over and over again, eventually the same test cases will no longer find new bugs. This is what pesticide paradox is actually referring to.

Recently, a good friend of mine (Adebola Oke), threw the idea to try and use faker for using dynamic test data with a choice to pick locale as well in our tests. Below is where you can read all about it on github.

https://github.com/DiUS/java-faker

I tried implementing the solution in our project and within first hour, I was absolutely in love with the solution. I knew it, this is going to be a standard part of how I write my tests from now on.

I could now go to a test class and rewrite my tests as below.

// Test Data
private Locale locale = new Locale("nl");
private Faker faker = new Faker(locale);

private String firstName = faker.name().firstName(); // Emory
private String lastName = faker.name().lastName(); // Barton
@Test
public void assertThatAPersonCanBeAddedByFillingOnlyMandatoryFields() {
person.setPersonalDetails("", lastName, firstName, "IN");
person.confirm();

assertWithPersonTable(sqlPerson, "", firstName, lastName, "IN");
}

If there are 20 tests and if they are all running in parallel, they would all get their own unique firstName and lastName as provided by faker. Pretty cool!

Takes care of the pesticide paradox for sure!

But I was not satisfied with this alone. What if tomorrow I wanted to test my application against say “en-IND” (Indian names) rather than “nl” (Netherlands names).

You say, pretty easy. Set a parameter in a config file and use that parameter here. Okay, that would take care of the issue of keeping tests flexible enough to pass any locale. But I was still not satisfied. I wanted my tests not only to be flexible enough so that at any time, a user could decide which locale user wanted to pass to tests (handy if its a must to test against a locale), but I also wanted a “random” mode where if choose a random mode, each of my tests in each of the class would run with a different random locale.

Now that would be a beast mode and take care of pesticide paradox at another level!

With this goal in mind, I created a class LocalePicker, which would decide if all my tests would run on a specific chosen fixed locale, or would run in a “random” beast mode or would give a default locale (if user messes up in giving a valid locale).

Below is the solution, that you would need to make it all work.

  1. You would need a file in your src -> main -> resources with somthing like this. The field of interest here is “locale”.

2. You would need a class that can read this config file and make it available to whoever will take a decision based on this parameter value (your choice of execution mode — random, a specified value say “nl” or default). I achieve this by creating a class using typesafe.config. Add below dependency in your pom file. A part of the pom file is shown below (showing version number and dependency used)

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>${maven.compiler.source}</maven.compiler.target>
<lombok.version>1.18.16</lombok.version>
<mysql.adapter.version>8.0.12</mysql.adapter.version>
<typesafe.version>1.4.1</typesafe.version>
<slf4j.version>1.7.30</slf4j.version>
<javafaker.version>1.0.2</javafaker.version>
</properties>
<!-- https://github.com/lightbend/config -->
<dependency>
<groupId>com.typesafe</groupId>
<artifactId>config</artifactId>
<version>${typesafe.version}</version>
</dependency>

3. A class that can now read the properties file using typesafe and make these properties available to tests.

A side note: I used typeconfig because it makes it possible to override the parameter values in CI (something that you would definitely want sooner than later).

Note: As is evident, above solution uses multiple file configs for each tests env. Say application.conf that contains common settings. A develop.conf that contains remote settings for running your tests on develop. A uat.conf that would contain remote settings for running your tests on uat. I showed an example of how application.conf looks above, the other files will look exactly similar with env specific values and file names that are used in above class to load their env specific properties.

4. Now you need a class that can pick the locale based on your chosen mode of execution (and run tests based on this chosen mode). Below class does that. So you have a beast “random” mode that would run each test with a different locale. A “chosen” mode, where all tests will run on the chosen selected mode and a “default” mode, in case if you screw up and give a wrong value in your properties file.

With all this, now your tests will look something like this below.

// Test Data
private Locale locale = new Locale(LocalePicker.getLocale());
private Faker faker = new Faker(locale);

private String firstName = faker.name().firstName(); // Emory
private String lastName = faker.name().lastName(); // Barton
@Test
public void assertThatAPersonCanBeAddedByFillingOnlyMandatoryFields() {
person.setPersonalDetails("", lastName, firstName, "IN");
person.confirm();

assertWithPersonTable(sqlPerson, "", firstName, lastName, "IN");
}

And now you truly have full control of how you want your tests to run. Look here for example the amount of variety it creates in my tests (all of which run in parallel with random locales now). Now rather than all my tests showing fixed first, last names, with every run, there is a new name generated as per the locale.

With this, I say bye bye to “pesticide paradox” — at least as far as data is concerned and welcome to a better way of using test data in your tests!

Happy learning!

--

--