diff --git a/README.md b/README.md index b33cf6e..c878422 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,18 @@ new WireMockServer(wireMockConfig().extensions(RandomExtension.class)); This will generate random first names in the `en-US` locale for every request. +You can optionally add: +* the `seed` parameter to always generate the same value for the same seed +* the `locale` parameter to generate values in a specific locale + +{% raw %} +```handle + +{% raw %} +```handlebars +{{ random 'Name.first_name' seed=1234 locale='es' }} +``` +%} ### Technical notes This library brings `net.datafaker:datafaker` as transitive dependency, which may result in conflicts at building time. diff --git a/src/main/java/org/wiremock/RandomHelper.java b/src/main/java/org/wiremock/RandomHelper.java index ea1171d..2cbcacf 100644 --- a/src/main/java/org/wiremock/RandomHelper.java +++ b/src/main/java/org/wiremock/RandomHelper.java @@ -2,30 +2,59 @@ import com.github.jknack.handlebars.Options; import com.github.tomakehurst.wiremock.extension.responsetemplating.helpers.HandlebarsHelper; +import java.util.Random; import net.datafaker.Faker; +import org.apache.commons.lang3.LocaleUtils; /* * Author: Shreya Agarwal */ public class RandomHelper extends HandlebarsHelper { + private static final String SEED_HASH = "seed"; + private static final String LOCALE_HASH = "locale"; + + private final Random random; private final Faker faker; public RandomHelper() { - faker = new Faker(); + this(new Random()); + } + + RandomHelper(Random random) { + this.random = random; + this.faker = new Faker(random); } @Override public Object apply(Object context, Options options) { + + // Use a seeded/localized/default Faker if `locale` or `seed` hash exist + final Faker myFaker = this.getFaker(options.hash(SEED_HASH), options.hash(LOCALE_HASH)); + try { // Used the `expression` method instead of `resolve` as the // resolve method is not able to resolve nested references in the yml files. For example, in // the resource file for en-US, `address.full_address` wasn't resolving because of the // `#{street_address}` nested reference, but `address.postcode_by_state.AL` would work fine. // `expression` is able to handle all cases. - return faker.expression("#{" + context + "}"); + return myFaker.expression("#{" + context + "}"); } catch (RuntimeException e) { return handleError("Unable to evaluate the expression " + context, e); } } + + private Faker getFaker(Object seed, String locale) { + final Faker myFaker; + if (seed != null && locale != null) { + myFaker = new Faker(LocaleUtils.toLocale(locale), new Random(seed.hashCode())); + } else if (seed != null) { + myFaker = new Faker(new Random(seed.hashCode())); + } else if (locale != null) { + myFaker = new Faker(LocaleUtils.toLocale(locale), this.random); + } else { + myFaker = this.faker; + } + return myFaker; + } } diff --git a/src/test/java/org/wiremock/RandomHelperTest.java b/src/test/java/org/wiremock/RandomHelperTest.java index 3a8a464..71407d0 100644 --- a/src/test/java/org/wiremock/RandomHelperTest.java +++ b/src/test/java/org/wiremock/RandomHelperTest.java @@ -5,9 +5,8 @@ import com.github.tomakehurst.wiremock.extension.responsetemplating.helpers.HandlebarsHelperTestBase; import java.io.IOException; -import java.lang.reflect.Field; +import java.util.HashMap; import java.util.Random; -import net.datafaker.Faker; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -18,7 +17,7 @@ public class RandomHelperTest extends HandlebarsHelperTestBase { @BeforeEach public void init() { - helper = new RandomHelper(); + helper = new RandomHelper(new Random(123456789L)); } @Test @@ -31,13 +30,37 @@ public void rendersAMeaningfulErrorWhenExpressionIsInvalid() throws IOException } @ParameterizedTest - @CsvSource(value = {"123456789, Name.firstName, Herb", "123456789, Name.lastName, Hauck"}) - public void returnsRandomValue(int seed, String expression, String expected) throws Exception { - Field newFaker = helper.getClass().getDeclaredField("faker"); - newFaker.setAccessible(true); - newFaker.set(helper, new Faker(new Random(seed))); - + @CsvSource( + value = { + "Name.firstName, Herb", + "Name.lastName, Hauck", + }) + public void returnsRandomValue(String expression, String expected) throws Exception { String actual = renderHelperValue(helper, expression); assertThat(actual, is(expected)); } + + @ParameterizedTest + @CsvSource( + value = { + ", , Herb", + ", es, José María", + ", fr_QC, Clément", + "1, , Donny", + "hello, , Nickie", + "test, de, Franz" + }) + public void rendersSeededLocalizedRandomValue(Object seed, Object locale, String expected) { + final var map = new HashMap(); + if (seed != null) { + map.put("seed", seed); + } + if (locale != null) { + map.put("locale", locale); + } + + final var actual = helper.apply("Name.firstName", createOptions(map)); + + assertThat(actual, is(expected)); + } }