Skip to content

Random Data Generators Java

Random Data Generators for API Testing in Java

One of the major problems of a test automation framework is the change of data that makes the tests flaky. Because if someone/or some process changes the data that was used by the test, the test would fail. One way to solve this issue is to create test data as part of the test itself. This makes test more robust because the data is now part of the test and it can’t change. In a previous post we discussed about the strategies to generate test data using POJOs and Lombok’s builder. This is a continuation of that post, but it’s more focused on the use of random data instead of using static values. With random data, we are communicating that the particular piece of data is not relevant to the specific test. We want to generate data to reduce the amount of boilerplate, and make tests clear in terms of what data is required to exercise the test without generating a bunch of noise that is not relevant to the test. In a sense, we aspire to create robust tests by using random values.

d2ac0c6431a0956d0d63fa1fdb2348c7.png

There are various ways to generate random data in Java, but here we will discuss mainly about Java Faker, JsonTemplate and random-beans libraries. We would see how we can use these libraries in conjunction with REST-assured to run an automated functional API test.

Java Faker

Java Faker can be used to generate a variety of real looking data. In order to use Java Faker in a project, we need to add the following maven dependency to our POM:

<dependency>
    <groupId>com.github.javafaker</groupId>
    <artifactId>javafaker</artifactId>
    <version>0.17.2</version>
</dependency>

Java Faker provides us with FakerValueService that in turn contains methods to generate random sequences of letters, numbers and both.

letterify(String letterString) method allows us to generate random alphabetic characters for the ? characters in theletterString that is passed to letterify() .

numerify(String numberString) method allows us to generate random numerical characters for the # characters in the numberString that is passed to numerify().

bothify(String alphanumericString) method allows us to generate random alphanumeric characters for the ? and # characters in the alphanumericString that is passed to bothify().

regexify(String regex) method allows us to generate a String that matches the given regular expression.

FakeValuesService faker = new FakeValuesService(
      new Locale("en-US"), new RandomService());
faker.letterify("12??89"); //will return something like "12hZ89"
faker.numerify("ABC##EF"); //will return something like "ABC99EF"
faker.bothify("12??##ED"); //will return something like "12iL27ED"
faker.regexify("[a-z1-9]{4}"); //will return something like "6bJ1"

Now, the question arises that how we can use the random values generated by the FakeValueService to create random data in our test. For this specific purpose, we can leverage POJOs that use Lombok’s @Builder annotation. Furthermore, we can use @Default annotation for the Builder to generate random data by default. So when we call POJO.builder().build() it should generate a POJO object with all fields auto populated by random values. One advantage of this approach is that we can override the random values in the builder to have a hybrid of random and hard coded values.

@Getter 
@Builder
public class Document {

   @Default
   private String title = faker.regexify("[a-z1-9]{10}");

   @Default
   private String socialTitle = faker.regexify("[a-z1-9]{10}");

   @Default
   private String heading = faker.regexify("[a-z1-9]{10}");

   @Default 
   private String description = faker.regexify("[a-z1-9]{10}");

}

@Test
public void testDefaultValues() {
    // this will generate a document with random values auto
    // populated for all fields as per the regex
    Document doc = Document.builder().build();
    Document response = given().
                  spec(RequestSpec).
                  body(doc).
                  post("/document").
                  as(Document.class);
    assertThat(response, sameBeanAs(doc));
}

@Test
public void testHybridValues() {
    // this will generate a document with random values 
    // overridden for the methods called in builder 
    Document doc = Document.builder().
                 .description("description")
                 .title("title")
                 .build();
    Document response = given().
                  spec(RequestSpec).
                  body(doc).
                  post("/document").
                  as(Document.class);
    assertThat(response, sameBeanAs(doc));
}

JsonTemplate

JsonTemplate is a tool to generate json Strings. JsonTemplate allows us to generate a schema-compatible json without bothering about the specific values. In order to use JsonTemplate in a project, we will need the following dependency in our POM:

<dependency>
    <groupId>com.github.json-template</groupId>
    <artifactId>jsontemplate</artifactId>
    <version>0.2.1</version>
</dependency>
In JsonTemplate, @x can refer to a value producer such as @s which generates a random String for you. There are other value producers such as @i for integers, @f for floats, @ip for an ip String. If a map parameter is given, the value producer produces a value according to the map values. For example, @s(min=10, max=20) will produce a String with a length between 10 and 20.

For the Document class given above, we can write a JsonTemplate and use in a test as following:

@Test
public void testJsonTemplate() {
      String template = new JsonTemplate(new StringBuilder()
          .append("{")
          .append("title:@s(min=10, max=20),")
          .append("socialTitle:@s(min=10, max=20),")
          .append("heading:@s(min=10, max=20),")
          .append("description:@s(min=10, max=20)")
          .append("}")
          .toString()).prettyString();
      Document doc = new Gson().fromJson(template,
                          Document.class);
      Document response = given().
                  spec(RequestSpec).
                  body(doc).
                  post("/document").
                  as(Document.class);
      assertThat(response, sameBeanAs(doc));
}

As we can see from above example that it’s not very easy to produce templates because it requires us to build a String first. If the json happens to be nested, then it can easily become a nightmare to maintain these templates.

Random Beans

With Random Beans we can generate beans with random values for all fields. It provides us with a nextObject(Class class) method, which produces a random Object of the class that was passed. One of the main advantages of Random Beans library is that it can create an Object graph of all the Objects used in a class with nested structure. In order to use Random Beans in a project, we will need the following dependency:

<dependency>
   <groupId>io.github.benas</groupId>
   <artifactId>random-beans</artifactId>
   <version>3.8.0</version>
</dependency>

Random Beans can be used to create random data for a test. Using EnhancedRandom class we can configure our random values for certain types as follows :

EnhancedRandom enhancedRandom = EnhancedRandomBuilder.aNewEnhancedRandomBuilder()
         .seed(123L)
         .objectPoolSize(100)
         .stringLengthRange(4, 10)
         .collectionSizeRange(1, 10)
         .scanClasspathForConcreteTypes(true)
         .build();

Once set, these parameters will be applied to all fields of the Object graph. stringLengthRange tells EnhancedRandom to generate a Strings with bounded size. When we set a seed value, each run produces the same random value. This feature is useful when one wants stable random values across the JVM restarts. We can learn more about these parameters here as well.

By default, Random Beans generates random values according to field type. However, we can use Randomizer interface to generate more meaningful random values. For instance, if we want to choose a random value from a set of values we can implement the Randomizer interface and override it’s getRandomValue() method to produce our custom random value as follows:

public class NumericalStringRandomizer implements Randomizer<String> {

   @Override
   public String getRandomValue() {
      return String.valueOf(new Random().nextInt(99000) + 1000);
   }

}

Using the above NumericalStringRandomizer we can get a random String with values ranging from “1000” to “100000” . In order to be able to use NumericalStringRandomizer we will need to annotate the field with @Randomizer in our class.

@Randomizer(NumericalStringRandomizer.class)
private String key;

If we want to exclude a particular field from producing a random value, we can annotate that field with @Exclude as follows:

@Exclude
private Long id;

Following is an example of a class that can generate a random bean with different type of randomizers that one can define:

@Getter
public class Document {
   @Exclude
   private Long id;   private String title;
   private String socialTitle;
   private String heading;

   @Randomizer(NumericalStringRandomizer.class)
   private String key;

   private String description;
}

We can use the class above in our REST-assured test as follows :

@Test
public void testDocumentForMetaData() {
      EnhancedRandom enhancedRandom = EnhancedRandomBuilder
         .aNewEnhancedRandomBuilder()
         .objectPoolSize(100)
         .stringLengthRange(4, 10)
         .build();
      Document doc = enhancedRandom.nextObject(Document.class);
      Document response = given().
                  spec(RequestSpec).
                  body(doc).
                  post("/document").
                  as(Document.class);
      assertThat(response, sameBeanAs(doc));
   });
}

One problem with using Random Beans to generate test data is that it requires us to create Randomizers for fields that require static values. There is not an easy work around for generating a random bean with both random and static values.

This was a brief analysis of some of a few random data generator java libraries available to use and how we could use them to generate test data. Hope you enjoyed reading the blog post and were able to learn about generating random test data from it. Thanks a lot for reading!


Source