Skip to content

Java lib that generates random data (numbers, strings, dates) - mostly to facilitate Randomized Testing.

License

Notifications You must be signed in to change notification settings

qala-io/datagen

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Datagen

Maven Central

Java library to generate random data (numbers, strings, dates) - to facilitate Randomized Testing. Randomization may be used to improve coverage, but it can also speed up the process of writing & running tests. Some use cases for randomization:

  • Combinatorial Testing - helps reducing the number of test cases you need to write and run
  • Fighting with Unique Constraints - do you have a test that registers users with unique username? And the next time you run the test it fails because such user already exists..
  • "Wow, I didn't know about that" effect - sometimes randomization may discover tricky cases that you couldn't think of.
  • Which one to pick? - often when choosing test data there is no clear winner of what value to pick. Randomization helps with that and ensures we're not prone to Pesticides Effect.

Example

import java.time.OffsetDateTime;

import static io.qala.datagen.RandomDate.beforeNow;
import static io.qala.datagen.RandomShortApi.*;

public class Dog {
    private String name;
    private OffsetDateTime timeOfBirth;
    private double weight;
    private double height;

    public static Dog random() {
        Dog dog = new Dog();
        dog.name = alphanumeric(1, 100);
        dog.timeOfBirth = nullOr(beforeNow().offsetDateTime());
        dog.weight = positiveDouble();
        dog.height = positiveInteger();
        return dog;
    }
}
@Alphanumeric(min = 2, max = 29, name = "middle value")
@Alphanumeric(length = 30, name = "max boundary")
@English(max=30)
void eachAnnotationInvokesTheTestOnceAndPassesParameters(String value, String name) {
    assertTrue(value.length() >= 1 && value.length() <= 31, "Failed case: " + name);
}

@RandomInt(min = 1, name = "greater than zero")
@RandomInt(max = -1, name = "less than zero")
void zeroInt_isNotPassed(int param, String name) {
    assertNotEquals(0, param, "Failed case: " + name);
}

Strings

import static io.qala.datagen.RandomValue.*;
import static io.qala.datagen.StringModifier.Impls.*;
import static io.qala.datagen.RandomShortApi.*;
Flexible API Short API Result
length(10).english() english(10) "DcRZUNPrED"
between(1, 10).alphanumeric() alphanumeric(0, 10) "zG9G"
between(1, 10).numeric() numeric(1, 10) "7167162"
length(5).unicode() unicode(5) "䂞ꂣ뢧䯺婜"
length(5).string("A_ B") " _B B"
length(10).with(specialSymbol()).english() "hOzKEV#iWv"
length(10).with(oneOf("_,")).english() "dwei,cNTfW"
length(5).with(spaces()).numeric() "874 9 "
length(3).with(spaceLeft()).english() " mT"
length(4).with(spacesRight(2)).english() "hF "
length(10).with(prefix("BLAH")).numeric() "BLAH453677"
between(1, 10).alphanumerics(4) ["cvA", "mTMDj0", "N", ""]
mixedCase("blah") "bLaH"

Nulls & Blanks

API Result
nullOrEmpty() "", null
nullOrBlank() "", " ", null
nullOr(10L) null, 10L
nullOr("string") null, "string"
blankOr("string") "", " ", null, "string"

Repeats

import static io.qala.datagen.RandomString.Type.*;
import static io.qala.datagen.RandomValue.*;
import static io.qala.datagen.Repeater.*;

repeat(length(4), NUMERIC).string("-").times(4)`

Result: "9338-8349-6940-7714"

Numbers

import static io.qala.datagen.RandomValue.*;
import static io.qala.datagen.RandomShortApi.*;
Flexible API Short API Result
between(0, 100).integer() integer(100) 89
between(-100, 100).integer() integer(-100, 100) -19
positiveInteger() 3432145
Long() 7635811362052252913
negativeDouble() -8.9946257128846746E18

Collections/Arrays

import static io.qala.datagen.RandomElements.*;
import static io.qala.datagen.RandomShortApi.*;
Flexible API Short API Result
from("A", "B", "C", "D").sample() sample("A", "B", "C") "C"
from("A", "B", "C", "D").sample(2) sampleMultiple(2, "A", "B", "C") ["B", "A"]
from("A", "B").sampleWithReplacement(3) ["A", "A", "B"]
from("A", "B", "C").shuffled() shuffled("A", "B", "C") ["C", "A", "B"]

Java Date

import static io.qala.datagen.RandomValue.*;

SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
between(f.parse("2015-01-01"), f.parse("2016-01-01")).date();

Result: 2015-11-30T08:33:20.349

Java8 DateTime

// Requires Java8 and qala-datagen-java8types dependency
import static io.qala.datagen.RandomDate.*;
API Result
plusMinus100Years().zonedDateTime() 1937-09-27T01:16:15.925440485+01:00[Europe/Belgrade]
since(yearAgo()).instant() 2015-11-30T08:39:28.397051483Z
before(now()).instant() -241279778-02-14T16:07:18.061693370Z
between(yearsAgo(2), startOfMonth()).localDate() 2014-09-30

Booleans

import static io.qala.datagen.RandomShortApi.*;
API Result
bool() or weighedTrue(0.5) false
bools(4) [false, true, true, false]
nullableBool() Boolean.TRUE

Functions (for Java8 lambdas)

import static io.qala.datagen.RandomShortApi.*;
Person person = new Person();

callOneOf(() -> person.firstName = english(5),
          () -> person.lastName = english(5));

Result: Person[null, "PDGRq"]

callNoneOrMore(() -> person.firstName = english(5),
               () -> person.lastName = english(5));

Result: Person[null, null]

callOneOrMore(() -> person.firstName = english(5),
              () -> person.lastName = english(5));

Result: Person["LjxYh", "UXoBt"]

Other

Special thanks

To keep the lib tiny and get rid of extra dependencies (there are no transitive dependencies) some of the code was borrowed from these libs: Commons Lang, Commons Math. Hail to open source!