From 4762d0aaf2c434b271d1b47082a2f457e4b5237b Mon Sep 17 00:00:00 2001 From: Markus Spann Date: Sat, 27 Jun 2026 21:16:03 +0200 Subject: [PATCH] Add @FakerSource annotation and FakerSourceProvider for dynamic test data generation Introduce a custom JUnit Jupiter ArgumentsProvider that dynamically resolves and executes Datafaker provider methods via reflection - Implement FakerSourceProvider supporting direct field/path expressions (e.g., 'address#longitude' or 'BELGIAN_FAKER.address') - Fall back to test class instantiation if testInstance is unavailable during the early lifecycle phase of non-static fields - Add support for combinatorial parameter arrays via multiParams - Migrate several tests (AddressTest, CodeTest, FinanceTest, HttpTest) from @RepeatedTest to @ParameterizedTest leveraging new @FakerSource --- .../java/net/datafaker/junit/FakerSource.java | 177 ++++++ .../datafaker/junit/FakerSourceProvider.java | 565 ++++++++++++++++++ .../junit/FakerSourceProviderTest.java | 241 ++++++++ .../datafaker/providers/base/AddressTest.java | 369 ++++++------ .../datafaker/providers/base/CodeTest.java | 130 ++-- .../datafaker/providers/base/FinanceTest.java | 110 ++-- .../datafaker/providers/base/HttpTest.java | 163 ++--- .../providers/base/IdNumberTest.java | 326 +++++----- .../providers/base/InternetTest.java | 234 ++++---- .../datafaker/providers/base/StockTest.java | 16 +- 10 files changed, 1675 insertions(+), 656 deletions(-) create mode 100644 src/test/java/net/datafaker/junit/FakerSource.java create mode 100644 src/test/java/net/datafaker/junit/FakerSourceProvider.java create mode 100644 src/test/java/net/datafaker/junit/FakerSourceProviderTest.java diff --git a/src/test/java/net/datafaker/junit/FakerSource.java b/src/test/java/net/datafaker/junit/FakerSource.java new file mode 100644 index 000000000..ab1f7b265 --- /dev/null +++ b/src/test/java/net/datafaker/junit/FakerSource.java @@ -0,0 +1,177 @@ +package net.datafaker.junit; + +import org.junit.jupiter.params.provider.ArgumentsSource; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * {@code @FakerSource} is an {@link ArgumentsSource} for + * {@link org.junit.jupiter.params.ParameterizedTest} that generates test arguments + * by invoking a Datafaker provider method via reflection. + * + *

Descriptor syntax

+ *

+ * Full form with parameters: + *

{@code
+ * code = "fieldName#methodName(ParamType1, ParamType2, ...)"
+ * }
+ *

+ * Short form for no-argument methods (parentheses optional): + *

{@code
+ * code = "fieldName#methodName"
+ * }
+ * + * + *

Parameter values

+ *

+ * Use {@link #params()} for a single value per parameter position, + * or {@link #multiParams()} for multiple alternative values per position. + * Both attributes cannot be combined. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
AttributeBehaviour
neither set (default)All constants of every declared enum type are used; + * their cartesian product forms the argument combinations.
{@code params = "VISA"}Shorthand for a single value per parameter position.
{@code multiParams = @Param({"DE", "AT"})}Multiple alternative values for one parameter position, + * producing one combination per value.
+ * + *

Locale

+ *

+ * When {@link #locale()} is set, a fresh {@link net.datafaker.Faker} for that locale + * is created and used as the source, overriding any field resolved from the test class. + * The value must be a valid IETF BCP 47 language tag (e.g. {@code "es-AR"}, {@code "nl-NL"}). + * + *

Examples

+ *
{@code
+ * // All CreditCardType constants × 100 repetitions
+ * @ParameterizedTest(name = "[{index}] {0}")
+ * @FakerSource(code = "finance#creditCard(CreditCardType)", repeat = 100)
+ * void allCardTypes(String card) { ... }
+ *
+ * // Only VISA, 100 repetitions
+ * @ParameterizedTest(name = "[{index}] {0}")
+ * @FakerSource(code = "finance#creditCard(CreditCardType)", params = "VISA", repeat = 100)
+ * void visaOnly(String card) { ... }
+ *
+ * // VISA and MASTERCARD via multiParams
+ * @ParameterizedTest(name = "[{index}] {0}")
+ * @FakerSource(code = "finance#creditCard(CreditCardType)", multiParams = @FakerSource.Param({"VISA", "MASTERCARD"}), repeat = 50)
+ * void twoTypes(String card) { ... }
+ *
+ * // Argentine zip codes – locale + distinct replaces @MethodSource
+ * @ParameterizedTest(name = "[{index}] {0}")
+ * @FakerSource(code = "address#zipCode", locale = "es-AR", repeat = 4025, distinct = true)
+ * void testArgentineZipCodes(String zipCode) { ... }
+ *
+ * // Provider object as argument (no method call)
+ * @ParameterizedTest(name = "[{index}] {0}")
+ * @FakerSource(code = "address", locale = "nl-BE", repeat = 10)
+ * void belgianAddress(Address address) { ... }
+ * }
+ */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@ArgumentsSource(FakerSourceProvider.class) +public @interface FakerSource { + + /** + * Reflection descriptor: {@code "fieldName#methodName(ParamType1, ParamType2, ...)"}. + *

+ * Type names are resolved in this order: + *

    + *
  1. Inner classes of the resolved provider (e.g. {@code Finance.CreditCardType})
  2. + *
  3. Fully-qualified class name
  4. + *
  5. Same package as the test class
  6. + *
  7. Inner or nested classes of the test class
  8. + *
+ */ + String code(); + + /** + * Shorthand for providing exactly one value per parameter position. + *

+ * Cannot be combined with {@link #multiParams()}. + *

+ * Each entry is resolved as: + *

+ */ + String[] params() default {}; + + /** + * Advanced form for providing multiple alternative values per parameter position.
+ * Each {@link Param} corresponds to one parameter position and lists the values + * to use for that position; their cartesian product forms the argument combinations. + *

+ * Cannot be combined with {@link #params()}. + */ + Param[] multiParams() default {}; + + /** + * IETF BCP 47 language tag for the Faker locale (e.g. {@code "es-AR"}, {@code "nl-NL"}). + *

+ * When set, a fresh {@link net.datafaker.Faker} with this locale is created and used + * as the source, overriding any field resolved from the test class. + * When empty (the default), the field resolved from the test class is used. + */ + String locale() default ""; + + /** + * When {@code true}, duplicate generated values are removed from the argument stream.
+ * Useful with a high {@link #repeat()} count to produce a set of unique values.
+ * Defaults to {@code false}. + */ + boolean distinct() default false; + + /** + * Number of times each argument combination is generated. Defaults to {@code 1}. + */ + int repeat() default 1; + + /** + * A list of alternative string values for a single parameter position, + * used with {@link FakerSource#multiParams()}. + * + *

+ * Example: + *

{@code
+     * @FakerSource(
+     *     code        = "finance#creditCard(CreditCardType)",
+     *     multiParams = @FakerSource.Param({"VISA", "MASTERCARD"}),
+     *     repeat      = 50
+     * )
+     * }
+ */ + @Retention(RetentionPolicy.RUNTIME) + @interface Param { + /** One or more string values for this parameter position. */ + String[] value(); + } + +} + diff --git a/src/test/java/net/datafaker/junit/FakerSourceProvider.java b/src/test/java/net/datafaker/junit/FakerSourceProvider.java new file mode 100644 index 000000000..9473aff0b --- /dev/null +++ b/src/test/java/net/datafaker/junit/FakerSourceProvider.java @@ -0,0 +1,565 @@ +package net.datafaker.junit; + +import net.datafaker.Faker; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.support.AnnotationConsumer; +import org.junit.jupiter.params.support.ParameterDeclarations; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +/** + * {@link ArgumentsProvider} backing {@link FakerSource}. + *

+ * Resolves datafaker provider methods dynamically. + * + *

Path resolution strategy

+ *

+ * The descriptor path (before {@code #}) is resolved left-to-right as a chain. + * Each segment is tried in this order: + *

    + *
  1. Instance field on the test class (or its superclasses)
  2. + *
  3. Static field on the test class (or its superclasses)
  4. + *
  5. No-arg method call on the current object (for chaining, e.g. {@code faker.address()})
  6. + *
+ */ +public class FakerSourceProvider implements ArgumentsProvider, AnnotationConsumer { + + // "field#method(ParamType)" or "field.chain#method" or "field.chain" or "field#method" + private static final Pattern DESCRIPTOR = Pattern.compile("^([\\w.]+)(?:#(\\w+))?(?:\\(([^)]*)\\))?$"); + + private String code; + private String[] params; + private FakerSource.Param[] multiParams; + private String locale; + private boolean distinct; + private int repeat; + + @Override + public void accept(FakerSource annotation) { + code = annotation.code().trim(); + params = annotation.params(); + multiParams = annotation.multiParams(); + locale = annotation.locale().trim(); + distinct = annotation.distinct(); + repeat = Math.max(1, annotation.repeat()); + + if (this.params.length > 0 && this.multiParams.length > 0) { + throw new FakerSourceException("specify either 'params' or 'multiParams', but not both"); + } + } + + /** + * Overrides the new JUnit 5.12 entry point to avoid the deprecation-driven + * {@code JUnitException} ("does not override provideArguments(ParameterDeclarations,…)"). + * Delegates to the classic {@link #provideArguments(ExtensionContext)} so all + * existing logic stays in one place. + */ + @Override + public Stream provideArguments(ParameterDeclarations parameters, ExtensionContext context) throws Exception { + return provideArguments(context); + } + + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + Class testClass = context.getRequiredTestClass(); + Object testInstance = context.getTestInstance().orElse(null); + + Descriptor desc = parseDescriptor(code); + Object provider = locale.isEmpty() + ? resolvePath(testClass, testInstance, desc.path()) + : resolvePathOnFaker(createLocaleFaker(), desc.path()); + + String targetMethod = desc.methodName(); + + // No "#method" in descriptor → the provider object itself is the result + // (e.g. "BELGIAN_FAKER.address" returns an Address provider directly) + if (targetMethod == null) { + List result = new ArrayList<>(repeat); + for (int i = 0; i < repeat; i++) { + result.add(Arguments.of(provider)); + } + Stream stream = result.stream(); + if (distinct) { + Set seen = new LinkedHashSet<>(); + stream = stream.filter(a -> seen.add(a.get()[0])); + } + return stream; + } + + Class targetClass = provider.getClass(); + List> valuesPerPosition = resolveValuesPerPosition(desc, testClass, targetClass); + List combos = createCartesianProduct(valuesPerPosition); + + List result = new ArrayList<>(combos.size() * repeat); + for (Object[] combo : combos) { + Method method = resolveMethod(targetClass, targetMethod, combo); + method.setAccessible(true); + + for (int i = 0; i < repeat; i++) { + Object fakerOutput = method.invoke(provider, combo); + + // test method receives: [fakerOutput, param1, param2, ...] + Object[] testArgs = new Object[combo.length + 1]; + testArgs[0] = fakerOutput; + System.arraycopy(combo, 0, testArgs, 1, combo.length); + + result.add(Arguments.of(testArgs)); + } + } + Stream stream = result.stream(); + if (distinct) { + Set seen = new LinkedHashSet<>(); + stream = stream.filter(a -> seen.add(a.get()[0])); + } + return stream; + } + + /** + * Resolves a dot-separated path against the test class. + *

+ * Each segment is resolved in order: + *

    + *
  1. Instance field on the test class hierarchy
  2. + *
  3. Static field on the test class hierarchy
  4. + *
  5. No-arg method call on the current object
  6. + *
+ *

+ * Example paths: + *

    + *
  • {@code "faker"} → field {@code faker}
  • + *
  • {@code "address"} → field {@code faker}, then {@code faker.address()}
  • + *
  • {@code "NL_FAKER.address"} → static field {@code NL_FAKER}, then {@code .address()}
  • + *
  • {@code "NL_FAKER.address()} → same, explicit call syntax
  • + *
+ */ + private Object resolvePath(Class testClass, Object testInstance, String path) throws Exception { + String[] segments = path.split("\\."); + Object current = null; + + for (int i = 0; i < segments.length; i++) { + String seg = segments[i].replace("()", "").trim(); // tolerate trailing () + + if (i == 0) { + // first segment: look for a field (instance or static) + Field field = findField(testClass, seg); + if (field != null) { + current = getFieldValue(field, testClass, testInstance); + } else { + // No field found – treat the first segment as a no-arg method + // on the 'faker' field (e.g. code = "address#latitude" where + // 'address' is not a field but faker.address() exists). + current = invokeViaFakerField(testClass, testInstance, seg); + } + } else { + // subsequent segments: chain as no-arg method calls on current + current = invokeNoArgMethod(current, seg); + } + } + + return current; + } + + /** + * Creates a fresh Faker for the configured locale. + * + * @return the instantiated Faker object + */ + private Object createLocaleFaker() { + try { + return new Faker(Locale.forLanguageTag(locale)); + } catch (Exception ex) { + throw new FakerSourceException(ex, "cannot create Faker for locale '%s'", locale); + } + } + + /** + * Like {@link #resolvePath} but starts from an externally provided object + * (the locale-specific Faker) instead of the test class fields. + * + * @param faker the root object to start navigation from + * @param path the dot-separated path to resolve + * @return the resolved object value + * @throws Exception if reflection invocation fails + */ + private Object resolvePathOnFaker(Object faker, String path) throws Exception { + String[] segments = path.split("\\."); + Object current = faker; + for (String segment : segments) { + current = invokeNoArgMethod(current, segment.replace("()", "").trim()); + } + return current; + } + + /** + * Resolves a name that is not a field by looking for a no-arg method with + * that name on the 'faker' instance field. + * + * @param testClass the class under test + * @param testInstance the instance under test, might be null + * @param methodName name of the method to look for on the faker field + * @return the object returned by the invoked method + * @throws Exception if target field or method cannot be read/invoked + */ + private Object invokeViaFakerField(Class testClass, Object testInstance, String methodName) throws Exception { + Field fakerField = findField(testClass, "faker"); + if (fakerField != null) { + Object fakerInstance = getFieldValue(fakerField, testClass, testInstance); + if (fakerInstance != null) { + try { + Method m = fakerInstance.getClass().getMethod(methodName); + m.setAccessible(true); + return m.invoke(fakerInstance); + } catch (NoSuchMethodException ignored) {} + } + } + throw new FakerSourceException("cannot resolve '%s' – not a field of %s and not a no-arg method on 'faker'", + methodName, testClass.getSimpleName()); + } + + /** + * Calls a public no-arg method on {@code target}. + * + * @param target the object to invoke the method on + * @param methodName the name of the method + * @return the method invocation result + * @throws Exception if invocation fails + */ + private Object invokeNoArgMethod(Object target, String methodName) throws Exception { + try { + Method m = target.getClass().getMethod(methodName); + m.setAccessible(true); + return m.invoke(target); + } catch (NoSuchMethodException ex) { + throw new FakerSourceException("no no-arg method '%s' on %s", methodName, target.getClass().getName()); + } + } + + /** + * Gathers all valid parameter values per parameter index based on the descriptor configuration. + * + * @param desc the parsed descriptor + * @param testClass the current test class context + * @param providerClass the class of the resolved datafaker provider + * @return a list of lists containing available values per position + */ + private List> resolveValuesPerPosition(Descriptor desc, Class testClass, Class providerClass) { + List> result = new ArrayList<>(); + int paramCount = desc.paramTypeNames().size(); + + for (int pos = 0; pos < paramCount; pos++) { + String typeStr = desc.paramTypeNames().get(pos); + Class paramType = resolveType(typeStr, testClass, providerClass); + + if (pos < params.length) { + // shortcut path: flat params array + result.add(List.of(resolveSingleParamEntry(params[pos], paramType))); + } else if (pos < multiParams.length) { + // advanced path: multiParams array + List resolvedValues = new ArrayList<>(); + for (String literal : multiParams[pos].value()) { + resolvedValues.add(resolveSingleParamEntry(literal, paramType)); + } + result.add(resolvedValues); + } else if (paramType.isEnum()) { + result.add(allConstants(paramType)); + } else { + throw new FakerSourceException("missing value for non-enum parameter type '%s'", typeStr); + } + } + return result; + } + + /** + * Casts or parses a single string literal value to its proper target type wrapper. + * + * @param entry the raw string literal value + * @param paramType the expected target type class + * @return the type-safe value object + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + private Object resolveSingleParamEntry(String entry, Class paramType) { + String trimmed = entry.trim(); + if (paramType.isEnum()) { + String constantName = trimmed.contains(".") ? trimmed.substring(trimmed.lastIndexOf('.') + 1) : trimmed; + return Enum.valueOf((Class) paramType, constantName); + } else if (paramType == short.class || paramType == Short.class) { + return Short.parseShort(trimmed); + } else if (paramType == int.class || paramType == Integer.class) { + return Integer.parseInt(trimmed); + } else if (paramType == long.class || paramType == Long.class) { + return Long.parseLong(trimmed); + } else if (paramType == boolean.class || paramType == Boolean.class) { + return Boolean.parseBoolean(trimmed); + } + return trimmed; + } + + /** + * Resolves a target type class by checking built-in primitives, provider inner classes, + * fully qualified names, package names and test class inner scopes. + * + * @param name simple or qualified name of the type + * @param context the test class context + * @param providerClass the provider target class + * @return the resolved class object + */ + private Class resolveType(String name, Class context, Class providerClass) { + switch (name) { + case "int" -> { return int.class; } + case "long" -> { return long.class; } + case "boolean" -> { return boolean.class; } + case "String", "java.lang.String" -> { return String.class; } + } + + // "Outer.Inner" syntax: resolve Outer first, then walk into Inner + if (name.contains(".") && !name.contains("$")) { + String[] parts = name.split("\\.", 2); + Class resolved = resolveTypeSimple(parts[0], context, providerClass); + if (resolved != null) { + return findNestedClass(resolved, parts[1]); + } + } + + Class result = resolveTypeSimple(name, context, providerClass); + if (result != null) { + return result; + } + throw new FakerSourceException("cannot resolve type '%s'", name); + } + + /** + * Resolves a simple (non-dotted) type name against the provider, classpath, + * test package, and test class inner types. Returns {@code null} if not found. + */ + private Class resolveTypeSimple(String name, Class context, Class providerClass) { + // inner classes of the resolved provider (e.g. Finance.CreditCardType) + for (Class inner : providerClass.getDeclaredClasses()) { + if (inner.getSimpleName().equals(name)) { + return inner; + } + } + try { + return Class.forName(name); + } catch (ClassNotFoundException ignored) {} + + try { + return Class.forName(context.getPackageName() + "." + name); + } catch (ClassNotFoundException ignored) {} + + for (Class inner : context.getDeclaredClasses()) { + if (inner.getSimpleName().equals(name)) { + return inner; + } + } + return null; + } + + /** + * Walks into a nested class by simple name, supporting further dot-separated segments. + * e.g. findNestedClass(Http.class, "Browser") returns Http.Browser. + */ + private Class findNestedClass(Class outer, String simpleName) { + if (simpleName.contains(".")) { + String[] parts = simpleName.split("\\.", 2); + for (Class inner : outer.getDeclaredClasses()) { + if (inner.getSimpleName().equals(parts[0])) { + return findNestedClass(inner, parts[1]); + } + } + } else { + for (Class inner : outer.getDeclaredClasses()) { + if (inner.getSimpleName().equals(simpleName)) { + return inner; + } + } + } + throw new FakerSourceException("cannot resolve nested type '%s' on %s", simpleName, outer.getSimpleName()); + } + + /** + * Parses the string-based code descriptor into structured path and parameter elements. + * + * @param code raw code from the annotation attribute + * @return a structured Descriptor instance + */ + private Descriptor parseDescriptor(String code) { + Matcher m = DESCRIPTOR.matcher(code); + if (!m.matches()) { + throw new FakerSourceException("cannot parse descriptor '%s'", code); + } + String path = m.group(1); + String methodName = m.group(2); // null if absent + String rawParams = Optional.ofNullable(m.group(3)).map(String::trim).orElse(""); + List paramNames = rawParams.isEmpty() ? List.of() : Arrays.asList(rawParams.split("\\s*,\\s*")); + + return new Descriptor(path, methodName, paramNames); + } + + /** + * Retrieves all enum constants of a target enum type. + * + * @param enumType the enum class + * @return a list containing all declared constants + */ + private List allConstants(Class enumType) { + return Arrays.asList(enumType.getEnumConstants()); + } + + /** + * Standard cartesian product builder to find all combinations across parameter positions. + * + * @param valuesPerPosition list of lists of available arguments per position + * @return list of mixed object array combinations + */ + private List createCartesianProduct(List> valuesPerPosition) { + List result = new ArrayList<>(); + result.add(new Object[0]); + for (List values : valuesPerPosition) { + List next = new ArrayList<>(result.size() * values.size()); + for (Object[] existing : result) { + for (Object value : values) { + Object[] combined = Arrays.copyOf(existing, existing.length + 1); + combined[existing.length] = value; + next.add(combined); + } + } + result = next; + } + return result; + } + + /** + * Reads the current value from a reflection field handling static vs. instance scopes. + * + * @param field reflection field target + * @param testClass class context + * @param testInstance current test instance, might be null + * @return field value contents + * @throws Exception if access or initialization fails + */ + private Object getFieldValue(Field field, Class testClass, Object testInstance) throws Exception { + field.setAccessible(true); + if (Modifier.isStatic(field.getModifiers())) { + return field.get(null); + } + if (testInstance != null) { + return field.get(testInstance); + } + Class declaring = field.getDeclaringClass(); + try { + var constructor = declaring.getDeclaredConstructor(); + constructor.setAccessible(true); + return field.get(constructor.newInstance()); + } catch (Exception ex) { + throw new FakerSourceException(ex, "cannot instantiate %s to read field '%s'", + declaring.getSimpleName(), field.getName()); + } + } + + /** + * Recursively searches for a field name upwards through the class hierarchy. + * + * @param clazz the starting class context + * @param name field name + * @return matching reflection field or null if not found + */ + private Field findField(Class clazz, String name) { + for (Class c = clazz; c != null; c = c.getSuperclass()) { + try { + return c.getDeclaredField(name); + } catch (NoSuchFieldException ignored) {} + } + return null; + } + + /** + * Identifies a target method that matches name and signature arguments. + * + * @param providerClass targeted provider class + * @param methodName method name to match + * @param args target values array + * @return matching method metadata + * @throws NoSuchMethodException if no compatible method signature exists + */ + private Method resolveMethod(Class providerClass, String methodName, Object[] args) throws NoSuchMethodException { + Class[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new); + for (Method method : providerClass.getMethods()) { + if (method.getName().equals(methodName) && matchParameters(method.getParameterTypes(), argTypes)) { + return method; + } + } + throw new FakerSourceException("no compatible method '%s' found on %s", methodName, providerClass.getName()); + } + + /** + * Verifies array length equality and calls single type assignability checks. + * + * @param declared declared method argument types + * @param actual runtime value argument types + * @return true if all match up + */ + private boolean matchParameters(Class[] declared, Class[] actual) { + if (declared.length != actual.length) { + return false; + } + for (int i = 0; i < declared.length; i++) { + if (!isAssignable(declared[i], actual[i])) { + return false; + } + } + return true; + } + + /** + * Type assignability logic honoring primitive types auto-boxing equivalencies. + * + * @param target required signature type + * @param source provided data type + * @return true if assignment is valid + */ + private boolean isAssignable(Class target, Class source) { + if (target.isAssignableFrom(source)) { + return true; + } else if (target == short.class && source == Short.class) { + return true; + } else if (target == int.class && source == Integer.class) { + return true; + } else if (target == long.class && source == Long.class) { + return true; + } + return target == boolean.class && source == Boolean.class; + } + + private record Descriptor(String path, String methodName, List paramTypeNames) {} + + /** + * Runtime exception dedicated to parsing and execution errors inside the provider. + */ + static final class FakerSourceException extends RuntimeException { + private static final long serialVersionUID = 1L; + private static final String ANNOT_NAME = "@" + FakerSource.class.getSimpleName(); + + FakerSourceException(String msgPattern, Object... args) { + super(ANNOT_NAME + ": " + String.format(msgPattern, args)); + } + + FakerSourceException(Throwable cause, String msgPattern, Object... args) { + super(ANNOT_NAME + ": " + String.format(msgPattern, args), cause); + } + } + +} diff --git a/src/test/java/net/datafaker/junit/FakerSourceProviderTest.java b/src/test/java/net/datafaker/junit/FakerSourceProviderTest.java new file mode 100644 index 000000000..d028baf27 --- /dev/null +++ b/src/test/java/net/datafaker/junit/FakerSourceProviderTest.java @@ -0,0 +1,241 @@ +package net.datafaker.junit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.support.ParameterDeclarations; + +import java.util.List; +import java.util.Optional; + +/** + * Unit tests for {@link FakerSourceProvider}. + */ +class FakerSourceProviderTest { + + /** Builds a fully configured mock annotation with sensible defaults. */ + private FakerSource createAnnotation(String code, String locale, int repeat, + boolean distinct, String[] params) { + FakerSource a = mock(FakerSource.class); + when(a.code()).thenReturn(code); + when(a.locale()).thenReturn(locale); + when(a.repeat()).thenReturn(repeat); + when(a.distinct()).thenReturn(distinct); + when(a.params()).thenReturn(params); + when(a.multiParams()).thenReturn(new FakerSource.Param[0]); + return a; + } + + /** Builds an ExtensionContext pointing at the given test class. */ + private ExtensionContext createContext(Class testClass, Object testInstance) { + ExtensionContext ctx = mock(ExtensionContext.class); + when(ctx.getRequiredTestClass()).thenAnswer(__ -> testClass); + when(ctx.getTestInstance()).thenReturn(Optional.ofNullable(testInstance)); + return ctx; + } + + /** Runs provideArguments and collects results into a list. */ + private List run(FakerSourceProvider provider, + Class testClass, + Object testInstance) throws Exception { + ParameterDeclarations params = mock(ParameterDeclarations.class); + return provider.provideArguments(params, createContext(testClass, testInstance)).toList(); + } + + @Test + void accept_throwsFakerSourceException_whenBothParamsAndMultiParamsAreSet() { + FakerSource a = mock(FakerSource.class); + when(a.code()).thenReturn("address#latitude"); + when(a.locale()).thenReturn(""); + when(a.repeat()).thenReturn(1); + when(a.distinct()).thenReturn(false); + when(a.params()).thenReturn(new String[]{"VISA"}); + FakerSource.Param param = mock(FakerSource.Param.class); + when(a.multiParams()).thenReturn(new FakerSource.Param[]{param}); + + assertThatThrownBy(() -> new FakerSourceProvider().accept(a)) + .isInstanceOf(FakerSourceProvider.FakerSourceException.class) + .hasMessageContaining("specify either 'params' or 'multiParams', but not both"); + } + + @Test + void provideArguments_resolvesStaticField_whenNoTestInstanceAvailable() throws Exception { + FakerSourceProvider provider = new FakerSourceProvider(); + provider.accept(createAnnotation("STUB_PROVIDER", "", 1, false, new String[0])); + + List args = run(provider, DummyTestClass.class, null); + + assertThat(args).hasSize(1); + assertThat(args.get(0).get()[0]).isEqualTo("ResolvedStubInstance"); + } + + @Test + void provideArguments_resolvesStaticField_whenTestInstancePresent() throws Exception { + FakerSourceProvider provider = new FakerSourceProvider(); + provider.accept(createAnnotation("STUB_PROVIDER", "", 1, false, new String[0])); + + List args = run(provider, DummyTestClass.class, new DummyTestClass()); + + assertThat(args).hasSize(1); + assertThat(args.get(0).get()[0]).isEqualTo("ResolvedStubInstance"); + } + + @Test + void provideArguments_resolvesInstanceField_whenTestInstancePresent() throws Exception { + FakerSourceProvider provider = new FakerSourceProvider(); + provider.accept(createAnnotation("instanceValue", "", 1, false, new String[0])); + + DummyTestClass instance = new DummyTestClass(); + List args = run(provider, DummyTestClass.class, instance); + + assertThat(args).hasSize(1); + assertThat(args.get(0).get()[0]).isEqualTo("InstanceValue"); + } + + @Test + void provideArguments_resolvesInstanceField_byInstantiatingTestClass_whenNoTestInstance() throws Exception { + FakerSourceProvider provider = new FakerSourceProvider(); + provider.accept(createAnnotation("instanceValue", "", 1, false, new String[0])); + + List args = run(provider, DummyTestClass.class, null); + + assertThat(args).hasSize(1); + assertThat(args.get(0).get()[0]).isEqualTo("InstanceValue"); + } + + @Test + void provideArguments_repeatsArguments_accordingToRepeatAttribute() throws Exception { + FakerSourceProvider provider = new FakerSourceProvider(); + provider.accept(createAnnotation("STUB_PROVIDER", "", 5, false, new String[0])); + + List args = run(provider, DummyTestClass.class, null); + + assertThat(args).hasSize(5) + .allMatch(a -> "ResolvedStubInstance".equals(a.get()[0])); + } + + @Test + void provideArguments_deduplicatesResults_whenDistinctIsTrue() throws Exception { + FakerSourceProvider provider = new FakerSourceProvider(); + // greet() always returns the same String → repeat=10 + distinct=true → exactly 1 result + provider.accept(createAnnotation("STUB_PROVIDER_WITH_METHOD#greet", "", 10, true, new String[0])); + + List args = run(provider, DummyTestClass.class, null); + + assertThat(args).hasSize(1); + assertThat(args.get(0).get()[0]).isEqualTo("Hello from stub"); + } + + @Test + void provideArguments_doesNotDeduplicate_whenDistinctIsFalse() throws Exception { + FakerSourceProvider provider = new FakerSourceProvider(); + // greet() always returns the same String → repeat=10 + distinct=false → 10 results + provider.accept(createAnnotation("STUB_PROVIDER_WITH_METHOD#greet", "", 10, false, new String[0])); + + List args = run(provider, DummyTestClass.class, null); + + assertThat(args).hasSize(10); + } + + @Test + void provideArguments_usesFakerWithLocale_whenLocaleIsSet() throws Exception { + FakerSourceProvider provider = new FakerSourceProvider(); + // address#countryCode returns a non-empty String for any locale + provider.accept(createAnnotation("address#countryCode", "en-US", 3, false, new String[0])); + + List args = run(provider, DummyTestClass.class, null); + + assertThat(args).hasSize(3) + .allSatisfy(a -> + assertThat((String) a.get()[0]).isNotBlank() + ); + } + + @Test + void provideArguments_ignoresTestClassFields_whenLocaleIsSet() throws Exception { + // DummyTestClass has no 'faker' field, but locale mode creates its own Faker + FakerSourceProvider provider = new FakerSourceProvider(); + provider.accept(createAnnotation("address#city", "en-US", 1, false, new String[0])); + + List args = run(provider, DummyTestClass.class, null); + + assertThat(args).hasSize(1); + assertThat(args.get(0).get()[0]).isInstanceOf(String.class); + } + + @Test + void provideArguments_invokesMethod_onResolvedProvider() throws Exception { + FakerSourceProvider provider = new FakerSourceProvider(); + provider.accept(createAnnotation("STUB_PROVIDER_WITH_METHOD#greet", "", 1, false, new String[0])); + + List args = run(provider, DummyTestClass.class, null); + + assertThat(args).hasSize(1); + assertThat(args.get(0).get()[0]).isEqualTo("Hello from stub"); + } + + @Test + void provideArguments_throwsFakerSourceException_whenPathCannotBeResolved() { + FakerSourceProvider provider = new FakerSourceProvider(); + provider.accept(createAnnotation("nonExistentField#method", "", 1, false, new String[0])); + + assertThatThrownBy(() -> run(provider, DummyTestClass.class, null)) + .isInstanceOf(FakerSourceProvider.FakerSourceException.class) + .hasMessageContaining("cannot resolve 'nonExistentField'"); + } + + @Test + void provideArguments_throwsFakerSourceException_whenDescriptorCannotBeParsed() { + FakerSource a = mock(FakerSource.class); + when(a.code()).thenReturn("!!!invalid!!!"); + when(a.locale()).thenReturn(""); + when(a.repeat()).thenReturn(1); + when(a.distinct()).thenReturn(false); + when(a.params()).thenReturn(new String[0]); + when(a.multiParams()).thenReturn(new FakerSource.Param[0]); + + FakerSourceProvider provider = new FakerSourceProvider(); + provider.accept(a); + + assertThatThrownBy(() -> run(provider, DummyTestClass.class, null)) + .isInstanceOf(FakerSourceProvider.FakerSourceException.class) + .hasMessageContaining("cannot parse descriptor"); + } + + @Test + void provideArguments_throwsFakerSourceException_whenLocaleIsInvalid() { + FakerSourceProvider provider = new FakerSourceProvider(); + // Faker may or may not throw for bad locales - test that exception is wrapped + // in FakerSourceException only if it throws at all. + // Using an obviously broken locale that triggers Faker to fail: + provider.accept(createAnnotation("address#city", "not-a-real-locale-xyz", 1, false, new String[0])); + + // either succeeds with a fallback locale or throws FakerSourceException - + // what it must NOT do is throw an unwrapped exception from Faker internals. + try { + List args = run(provider, DummyTestClass.class, null); + assertThat(args).isNotNull(); + } catch (Exception ex) { + assertThat(ex).isInstanceOf(FakerSourceProvider.FakerSourceException.class); + } + } + + static final class DummyTestClass { + static final Object STUB_PROVIDER = "ResolvedStubInstance"; + + static final StubProviderWMethod STUB_PROVIDER_WITH_METHOD = new StubProviderWMethod(); + + final String instanceValue = "InstanceValue"; + } + + static final class StubProviderWMethod { + public String greet() { + return "Hello from stub"; + } + } +} diff --git a/src/test/java/net/datafaker/providers/base/AddressTest.java b/src/test/java/net/datafaker/providers/base/AddressTest.java index 096a7b5d6..b18767180 100644 --- a/src/test/java/net/datafaker/providers/base/AddressTest.java +++ b/src/test/java/net/datafaker/providers/base/AddressTest.java @@ -1,37 +1,35 @@ package net.datafaker.providers.base; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import net.datafaker.Faker; +import net.datafaker.junit.FakerSource; import org.assertj.core.api.Condition; -import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.ValueSource; import java.text.DecimalFormatSymbols; -import java.util.Collection; import java.util.Locale; import java.util.Random; import java.util.function.BiFunction; import java.util.function.Function; import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; class AddressTest { - private final Faker faker = new Faker(); + private final Faker faker = new Faker(); + private final Address address = faker.address(); + private final char decimalSeparator = new DecimalFormatSymbols(faker.getContext().getLocale()).getDecimalSeparator(); - private static final Faker US_FAKER = new Faker(new Locale("en", "US")); - private static final Faker NL_FAKER = new Faker(new Locale("nl", "NL")); - private static final Faker BELGIAN_FAKER = new Faker(new Locale("nl", "BE")); - private static final Faker RU_FAKER = new Faker(new Locale("ru", "RU")); - private static final Faker AU_FAKER = new Faker(new Locale("en", "AU")); - private static final Faker IE_FAKER = new Faker(new Locale("en", "IE")); + + // Still needed for tests that use locale-specific decimal separators at assertion time + static final Faker RU_FAKER = new Faker(new Locale("ru", "RU")); + static final Faker US_FAKER = new Faker(new Locale("en", "US")); + static final Faker NL_FAKER = new Faker(new Locale("nl", "NL")); + private static final Pattern CYRILLIC_LETTERS = Pattern.compile(".*[а-яА-Я].*"); private static final Condition IS_A_NUMBER = new Condition<>(s -> { @@ -51,296 +49,293 @@ class AddressTest { (decimalDelimiter, delimiter) -> Pattern.compile("-?\\d{1,3}" + decimalDelimiter + "\\d{5,10}+" + delimiter + "-?\\d{1,2}" + decimalDelimiter + "\\d{5,10}"); - private final static Function ESCAPED_DECIMAL_SEPARATOR = t -> "\\" + new DecimalFormatSymbols(t).getDecimalSeparator(); + private static final Function ESCAPED_DECIMAL_SEPARATOR = + t -> "\\" + new DecimalFormatSymbols(t).getDecimalSeparator(); @ParameterizedTest @ValueSource(strings = {"en", "id", "ca", "cs"}) - void testLatinStreetName() { - final BaseFaker faker = new BaseFaker(); - assertThat(faker.address().streetName()).isNotEmpty().doesNotMatch(CYRILLIC_LETTERS); + void testLatinStreetName(String locale) { + assertThat(new BaseFaker(new Locale(locale)).address().streetName()) + .isNotEmpty().doesNotMatch(CYRILLIC_LETTERS); } @ParameterizedTest @ValueSource(strings = {"be", "bg", "by", "mk", "ru", "ru_MD", "uk"}) void testCyrillicStreetName(String cyrillicLocale) { - final BaseFaker localFaker = new BaseFaker(new Locale(cyrillicLocale)); - assertThat(localFaker.address().streetName()).isNotEmpty().matches(CYRILLIC_LETTERS); + assertThat(new BaseFaker(new Locale(cyrillicLocale)).address().streetName()) + .isNotEmpty().matches(CYRILLIC_LETTERS); } - @Test - void testStreetAddressStartsWithNumber() { - final String streetAddress = faker.address().streetAddress(); - assertThat(streetAddress).matches("[0-9]+ .+"); + @ParameterizedTest + @ValueSource(strings = {"bg", "ru", "uk", "by"}) + void cyrillicStreetName(String locale) { + assertThat(new BaseFaker(new Locale(locale)).address().streetName()) + .isNotEmpty().matches(CYRILLIC_LETTERS); } - @Test - void testStreetAddressNumberIsANumberGreaterZero() { - final String streetAddressNumber = faker.address().streetAddressNumber(); - assertThat(streetAddressNumber).matches("[1-9][0-9]*"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#latitude", repeat = 10) + void testLatitude(String latitude) { + String latStr = latitude.replace(decimalSeparator, '.'); + assertThat(latStr).is(IS_A_NUMBER); + assertThat(Double.valueOf(latStr)).isBetween(-90.0, 90.0); } - @Test - void usingOnlyCountryCodeWithoutLanguage() { - assertThat(new BaseFaker(new Locale("xx", "AL")).address().countryCode()).isEqualTo("AL"); - assertThat(new BaseFaker(new Locale("xx", "AM")).address().countryCode()).isEqualTo("AM"); - assertThat(new BaseFaker(new Locale("xx", "BG")).address().countryCode()).isEqualTo("BG"); - assertThat(new BaseFaker(new Locale("xx", "BY")).address().countryCode()).isEqualTo("BY"); - assertThat(new BaseFaker(new Locale("xx", "BY")).address().postcode()).matches("\\d{6}"); - assertThat(new BaseFaker(new Locale("xx", "EE")).address().postcode()).matches("\\d{5}"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#longitude", repeat = 10) + void testLongitude(String longitude) { + String longStr = longitude.replace(decimalSeparator, '.'); + assertThat(longStr).is(IS_A_NUMBER); + assertThat(Double.valueOf(longStr)).isBetween(-180.0, 180.0); } - @RepeatedTest(10) - void testLatitude() { - String latStr = faker.address().latitude().replace(decimalSeparator, '.'); - assertThat(latStr).is(IS_A_NUMBER); - Double lat = Double.valueOf(latStr); - assertThat(lat).isBetween(-90.0, 90.0); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#latitude", repeat = 10) + void testLocaleLatitude(String latitude) { + assertThat(latitude.replace(decimalSeparator, '.')).matches("-?\\d{1,3}\\.\\d+"); } - @RepeatedTest(10) - void testLongitude() { - String longStr = faker.address().longitude().replace(decimalSeparator, '.'); - assertThat(longStr).is(IS_A_NUMBER); - Double lon = Double.valueOf(longStr); - assertThat(lon).isBetween(-180.0, 180.0); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#longitude", repeat = 10) + void testLocaleLongitude(String longitude) { + assertThat(longitude.replace(decimalSeparator, '.')).matches("-?\\d{1,3}\\.\\d+"); } - @RepeatedTest(10) - void testLocaleLatitude() { - BaseFaker engFaker = new BaseFaker(Locale.ENGLISH); - String engLatStr = engFaker.address().latitude(); - assertThat(engLatStr).matches("-?\\d{1,3}\\.\\d+"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#latLon", locale = "en-US", repeat = 10) + void testLatLonEnUs(String latLon) { + assertThat(latLon).matches(BI_LAT_LON_REGEX.apply( + ESCAPED_DECIMAL_SEPARATOR.apply(new Locale("en", "US")), ",")); } - @RepeatedTest(10) - void testLocaleLongitude() { - BaseFaker engFaker = new BaseFaker(Locale.ENGLISH); - String engLatStr = engFaker.address().longitude(); - assertThat(engLatStr).matches("-?\\d{1,3}\\.\\d+"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#latLon", locale = "nl-NL", repeat = 10) + void testLatLonNl(String latLon) { + assertThat(latLon).matches(BI_LAT_LON_REGEX.apply( + ESCAPED_DECIMAL_SEPARATOR.apply(new Locale("nl", "NL")), ",")); } - @Test - void testTimeZone() { - assertThat(faker.address().timeZone()).matches("[A-Za-z_]+/[A-Za-z_]+[/A-Za-z_]*"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#lonLat", locale = "en-US", repeat = 10) + void testLonLatEnUs(String lonLat) { + assertThat(lonLat).matches(BI_LON_LAT_REGEX.apply( + ESCAPED_DECIMAL_SEPARATOR.apply(new Locale("en", "US")), ",")); } - @Test - void testState() { - assertThat(faker.address().state()).matches("[A-Za-z ]+"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#lonLat", locale = "nl-NL", repeat = 10) + void testLonLatNl(String lonLat) { + assertThat(lonLat).matches(BI_LON_LAT_REGEX.apply( + ESCAPED_DECIMAL_SEPARATOR.apply(new Locale("nl", "NL")), ",")); } @Test - void testCity() { - assertThat(faker.address().city()).matches("[A-Za-z'() ]+"); + void testLonLatRU() { + assertThat(RU_FAKER.address().lonLat(";")) + .matches(BI_LON_LAT_REGEX.apply(ESCAPED_DECIMAL_SEPARATOR.apply(RU_FAKER.getContext().getLocale()), ";")); } @Test - void testCityName() { - assertThat(faker.address().cityName()).matches("[A-Za-z'() ]+"); + void testLatLonRU() { + assertThat(RU_FAKER.address().latLon(";")) + .matches(BI_LAT_LON_REGEX.apply(ESCAPED_DECIMAL_SEPARATOR.apply(RU_FAKER.getContext().getLocale()), ";")); } @Test - void testCountry() { - assertThat(faker.address().country()).matches("[A-Za-z\\- &.,'()\\d]+"); + void testStreetAddressStartsWithNumber() { + assertThat(address.streetAddress()).matches("[0-9]+ .+"); } @Test - void testCountryCode() { - assertThat(faker.address().countryCode()).matches("[A-Za-z ]+"); + void testStreetAddressNumberIsANumberGreaterZero() { + assertThat(address.streetAddressNumber()).matches("[1-9][0-9]*"); } @Test void testStreetAddressIncludeSecondary() { - assertThat(faker.address().streetAddress(true)).isNotEmpty(); + assertThat(address.streetAddress(true)).isNotEmpty(); } @Test - void testCityWithLocaleFranceAndSeed() { - long seed = (long) (Long.MAX_VALUE * Math.random()); - BaseFaker firstFaker = new BaseFaker(Locale.FRANCE, new Random(seed)); - BaseFaker secondFaker = new BaseFaker(Locale.FRANCE, new Random(seed)); - for (int i = 0; i < 100; i++) { - assertThat(firstFaker.address().city()).isEqualTo(secondFaker.address().city()); - } + void testStreetPrefix() { + assertThat(address.streetPrefix()).isNotEmpty(); } @Test - void testFullAddress() { - assertThat(faker.address().fullAddress()).isNotEmpty(); + void testStreetSuffix() { + assertThat(address.streetSuffix()).isNotEmpty(); } @Test - void fullAddress_estonia() { - BaseFaker f = new BaseFaker(new Locale("et", "EE")); - assertThat(f.address().fullAddress()).isNotEmpty(); + void testCityPrefix() { + assertThat(address.cityPrefix()).isNotEmpty(); } @Test - void eirCode_ireland() { - BaseFaker f = new BaseFaker(new Locale("en", "IE")); - assertThat(f.address().eircode()).isNotEmpty(); + void testCitySuffix() { + assertThat(address.citySuffix()).isNotEmpty(); } @Test - void testZipCodeByState() { - final BaseFaker localFaker = new BaseFaker(new Locale("en", "US")); - assertThat(localFaker.address().zipCodeByState(localFaker.address().stateAbbr())).matches("[0-9]{5}"); + void testCity() { + assertThat(address.city()).matches("[A-Za-z'() ]+"); } @Test - void testHungarianZipCodeByState() { - final BaseFaker localFaker = new BaseFaker(new Locale("hu")); - assertThat(localFaker.address().zipCodeByState(localFaker.address().stateAbbr())).matches("[0-9]{4}"); + void testCityName() { + assertThat(address.cityName()).matches("[A-Za-z'() ]+"); } @Test - void testCountyByZipCode() { - final BaseFaker localFaker = new BaseFaker(new Locale("en", "US")); - assertThat(localFaker.address().countyByZipCode("47732")).isNotEmpty(); + void testState() { + assertThat(address.state()).matches("[A-Za-z ]+"); } - static Collection argentineZipCodesSource() { - return Stream.generate(() -> new BaseFaker(Locale.forLanguageTag("es-AR")).address().zipCode()) - .distinct().limit(4025) - .collect(Collectors.toSet()); + @Test + void testCountry() { + assertThat(address.country()).matches("[A-Za-z\\- &.,'()\\d]+"); } - @ParameterizedTest - @MethodSource("argentineZipCodesSource") - void testArgentineZipCodes(String zipCode) { - assertThat(zipCode).matches("^[ABDEFGHJKLMNPQRSTUVWXYZ][0-9]{4}$"); + @Test + void testCountryCode() { + assertThat(address.countryCode()).matches("[A-Za-z ]+"); } - @ParameterizedTest - @NullSource - @ValueSource(strings = {"1", "asd", "qwe", "wrong"}) - void testCountyForWrongZipCode(String zipCode) { - final BaseFaker localFaker = new BaseFaker(new Locale("en", "US")); - assertThatThrownBy(() -> localFaker.address().countyByZipCode(zipCode)) - .isInstanceOf(RuntimeException.class) - .hasMessage("County is not configured for postcode " + zipCode); + @Test + void testTimeZone() { + assertThat(address.timeZone()).matches("[A-Za-z_]+/[A-Za-z_]+[/A-Za-z_]*"); } @Test - void testStreetPrefix() { - assertThat(faker.address().streetPrefix()).isNotEmpty(); + void testFullAddress() { + assertThat(address.fullAddress()).isNotEmpty(); } - @Test - void testStreetSuffix() { - assertThat(faker.address().streetSuffix()).isNotEmpty(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#mailBox", repeat = 10) + void testMailbox(String mailBox) { + assertThat(mailBox).matches("PO Box [0-9]{2,4}"); } @Test - void testCityPrefix() { - assertThat(faker.address().cityPrefix()).isNotEmpty(); + void testZipIsFiveChars() { + assertThat(new BaseFaker(new Locale("en", "US")).address().zipCode()).hasSize(5); } @Test - void testCitySuffix() { - assertThat(faker.address().citySuffix()).isNotEmpty(); + void testZipPlus4IsTenChars() { + assertThat(new BaseFaker(new Locale("en", "US")).address().zipCodePlus4()).hasSize(10); } - @RepeatedTest(10) - void testMailbox() { - assertThat(faker.address().mailBox()).matches("PO Box [0-9]{2,4}"); + @Test + void testZipPlus4IsNineDigits() { + String[] parts = new BaseFaker(new Locale("en", "US")).address().zipCodePlus4().split("-"); + assertThat(parts[0]).matches("[0-9]{5}"); + assertThat(parts[1]).matches("[0-9]{4}"); } @Test - void testZipIsFiveChars() { - final BaseFaker localFaker = new BaseFaker(new Locale("en", "US")); - assertThat(localFaker.address().zipCode()).hasSize(5); + void testZipCodeByState() { + BaseFaker localFaker = new BaseFaker(new Locale("en", "US")); + assertThat(localFaker.address().zipCodeByState(localFaker.address().stateAbbr())).matches("[0-9]{5}"); } @Test - void testZipPlus4IsTenChars() { - final BaseFaker localFaker = new BaseFaker(new Locale("en", "US")); - assertThat(localFaker.address().zipCodePlus4()).hasSize(10); // includes dash + void testHungarianZipCodeByState() { + BaseFaker localFaker = new BaseFaker(new Locale("hu")); + assertThat(localFaker.address().zipCodeByState(localFaker.address().stateAbbr())).matches("[0-9]{4}"); } @Test - void testZipPlus4IsNineDigits() { - final BaseFaker localFaker = new BaseFaker(new Locale("en", "US")); - final String[] zipCodeParts = localFaker.address().zipCodePlus4().split("-"); - assertThat(zipCodeParts[0]).matches("[0-9]{5}"); - assertThat(zipCodeParts[1]).matches("[0-9]{4}"); + void testCountyByZipCode() { + assertThat(new BaseFaker(new Locale("en", "US")).address().countyByZipCode("47732")).isNotEmpty(); } - @RepeatedTest(10) - void testLatLonEnUs() { - assertThat(US_FAKER.address().latLon()) - .matches(BI_LAT_LON_REGEX.apply(ESCAPED_DECIMAL_SEPARATOR.apply(US_FAKER.getContext().getLocale()), ",")); + @ParameterizedTest + @NullSource + @ValueSource(strings = {"1", "asd", "qwe", "wrong"}) + void testCountyForWrongZipCode(String zipCode) { + assertThatThrownBy(() -> new BaseFaker(new Locale("en", "US")).address().countyByZipCode(zipCode)) + .isInstanceOf(RuntimeException.class) + .hasMessage("County is not configured for postcode " + zipCode); } - @RepeatedTest(10) - void testLatLonNl() { - assertThat(NL_FAKER.address().latLon()) - .matches(BI_LAT_LON_REGEX.apply(ESCAPED_DECIMAL_SEPARATOR.apply(NL_FAKER.getContext().getLocale()), ",")); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#zipCode", locale = "es-AR", repeat = 4025, distinct = true) + void testArgentineZipCodes(String zipCode) { + assertThat(zipCode).matches("^[ABDEFGHJKLMNPQRSTUVWXYZ][0-9]{4}$"); } - @RepeatedTest(10) - void testLonLatEnUs() { - assertThat(US_FAKER.address().lonLat()) - .matches(BI_LON_LAT_REGEX.apply(ESCAPED_DECIMAL_SEPARATOR.apply(US_FAKER.getContext().getLocale()), ",")); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#fullAddress", locale = "nl-NL", repeat = 10) + void dutchAddress(String fullAddress) { + assertThat(fullAddress).matches("[A-Z].+, [0-9]{4} [A-Z]{2}, [A-Z].+"); } - @RepeatedTest(10) - void testLonLatNl() { - assertThat(NL_FAKER.address().lonLat()) - .matches(BI_LON_LAT_REGEX.apply(ESCAPED_DECIMAL_SEPARATOR.apply(NL_FAKER.getContext().getLocale()), ",")); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#stateAbbr", locale = "nl-BE", repeat = 10) + void belgianAddressStateAbbr(String stateAbbr) { + assertThat(stateAbbr).matches("[A-Z]{3}"); } - @Test - void testLonLatRU() { - assertThat(RU_FAKER.address().lonLat(";")) - .matches(BI_LON_LAT_REGEX.apply(ESCAPED_DECIMAL_SEPARATOR.apply(RU_FAKER.getContext().getLocale()), ";")); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#fullAddress", locale = "nl-BE", repeat = 10) + void belgianAddressFullAddress(String fullAddress) { + assertThat(fullAddress).matches("[A-Z].+, [0-9]{4}, [A-Z].+"); } - @Test - void testLatLonRU() { - assertThat(RU_FAKER.address().latLon(";")) - .matches(BI_LAT_LON_REGEX.apply(ESCAPED_DECIMAL_SEPARATOR.apply(RU_FAKER.getContext().getLocale()), ";")); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#zipCode", locale = "nl-BE", repeat = 10) + void belgianZipcode(String zipCode) { + assertThat(Integer.valueOf(zipCode)).isBetween(1000, 9992); } - @ParameterizedTest - @ValueSource(strings = {"bg", "ru", "uk", "by"}) - void cyrillicStreetName(String locale) { - BaseFaker localFaker = new BaseFaker(new Locale(locale)); - assertThat(localFaker.address().streetName()).isNotEmpty().matches(CYRILLIC_LETTERS); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#fullAddress", locale = "en-AU", repeat = 10) + void australiaAddress(String fullAddress) { + assertThat(fullAddress).matches("(Unit|[0-9]).+, [A-Z].+, [A-Z]{2,3} [0-9]{4}"); } - @RepeatedTest(10) - void dutchAddress() { - assertThat(NL_FAKER.address().stateAbbr()).matches("[A-Z]{2}"); - assertThat(NL_FAKER.address().fullAddress()).matches("[A-Z].+, [0-9]{4} [A-Z]{2}, [A-Z].+"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "address#fullAddress", locale = "en-IE", repeat = 10) + void irishAddress(String fullAddress) { + assertThat(fullAddress).matches( + "(?:.+, [A-Z].+, Co\\. [A-Z].+)|(?:.+, Dublin D[A-Z0-9]{2,3})|(?:.+, [A-Z].+, (?:Ireland|Republic of Ireland))"); } - @RepeatedTest(10) - void belgianSAddress() { - assertThat(BELGIAN_FAKER.address().stateAbbr()).matches("[A-Z]{3}"); - assertThat(BELGIAN_FAKER.address().fullAddress()).matches("[A-Z].+, [0-9]{4}, [A-Z].+"); + @Test + void usingOnlyCountryCodeWithoutLanguage() { + assertThat(new BaseFaker(new Locale("xx", "AL")).address().countryCode()).isEqualTo("AL"); + assertThat(new BaseFaker(new Locale("xx", "AM")).address().countryCode()).isEqualTo("AM"); + assertThat(new BaseFaker(new Locale("xx", "BG")).address().countryCode()).isEqualTo("BG"); + assertThat(new BaseFaker(new Locale("xx", "BY")).address().countryCode()).isEqualTo("BY"); + assertThat(new BaseFaker(new Locale("xx", "BY")).address().postcode()).matches("\\d{6}"); + assertThat(new BaseFaker(new Locale("xx", "EE")).address().postcode()).matches("\\d{5}"); } - @RepeatedTest(10) - void belgianZipcode() { - assertThat(Integer.valueOf(BELGIAN_FAKER.address().zipCode())).isBetween(1000, 9992); + @Test + void testCityWithLocaleFranceAndSeed() { + long seed = (long) (Long.MAX_VALUE * Math.random()); + BaseFaker firstFaker = new BaseFaker(Locale.FRANCE, new Random(seed)); + BaseFaker secondFaker = new BaseFaker(Locale.FRANCE, new Random(seed)); + for (int i = 0; i < 100; i++) { + assertThat(firstFaker.address().city()).isEqualTo(secondFaker.address().city()); + } } - @RepeatedTest(10) - void australiaAddress() { - assertThat(AU_FAKER.address().fullAddress()).matches("(Unit|[0-9]).+, [A-Z].+, [A-Z]{2,3} [0-9]{4}"); + @Test + void fullAddress_estonia() { + assertThat(new BaseFaker(new Locale("et", "EE")).address().fullAddress()).isNotEmpty(); } - @RepeatedTest(10) - void irishAddress() { - String regex = "(?:.+, [A-Z].+, Co\\. [A-Z].+)|" + - "(?:.+, Dublin D[A-Z0-9]{2,3})|" + - "(?:.+, [A-Z].+, (?:Ireland|Republic of Ireland))"; - assertThat(IE_FAKER.address().fullAddress()).matches(regex); + @Test + void eirCode_ireland() { + assertThat(new BaseFaker(new Locale("en", "IE")).address().eircode()).isNotEmpty(); } - @RepeatedTest(10) + @Test void testCityCnSuffix() { assertThat(new Faker(Locale.CHINA).address().citySuffix()).matches("[\\u4e00-\\u9fa5]{1,7}(?:省|自治区)"); } } + diff --git a/src/test/java/net/datafaker/providers/base/CodeTest.java b/src/test/java/net/datafaker/providers/base/CodeTest.java index 8ffba5d1b..41e43f1de 100644 --- a/src/test/java/net/datafaker/providers/base/CodeTest.java +++ b/src/test/java/net/datafaker/providers/base/CodeTest.java @@ -1,138 +1,136 @@ package net.datafaker.providers.base; +import static org.assertj.core.api.Assertions.assertThat; + import net.datafaker.Faker; +import net.datafaker.junit.FakerSource; import org.apache.commons.validator.routines.ISBNValidator; import org.apache.commons.validator.routines.checkdigit.EAN13CheckDigit; import org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; import java.util.Locale; -import static org.assertj.core.api.Assertions.assertThat; - class CodeTest { - private final Faker faker = new Faker(); - @RepeatedTest(100) - void isbn10DefaultIsNoSeparator() { - final BaseFaker faker = new BaseFaker(); - String isbn10 = faker.code().isbn10(); + private static final ISBNValidator ISBN_VALIDATOR = ISBNValidator.getInstance(false); + + private final Code code = new Faker().code(); - final ISBNValidator isbnValidator = ISBNValidator.getInstance(false); - assertIsValidISBN10(isbn10, isbnValidator); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "code#isbn10", repeat = 100) + void isbn10DefaultIsNoSeparator(String isbn10) { + assertIsValidISBN10(isbn10); assertThat(isbn10).doesNotContain("-"); } - @RepeatedTest(100) - void isbn13DefaultIsNoSeparator() { - final BaseFaker faker = new BaseFaker(); - String isbn13 = faker.code().isbn13(); - - final ISBNValidator isbnValidator = ISBNValidator.getInstance(false); - assertIsValidISBN13(isbn13, isbnValidator); - assertThat(isbn13).doesNotContain("-"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "code#isbn10(boolean)", params = "false", repeat = 100) + void isbn10WithoutSeparator(String isbn10) { + assertThat(isbn10).hasSize(10); + assertIsValidISBN10(isbn10); } - @RepeatedTest(100) - void testIsbn10() { - final BaseFaker faker = new BaseFaker(); - final String isbn10NoSep = faker.code().isbn10(false); - final String isbn10Sep = faker.code().isbn10(true); - final ISBNValidator isbnValidator = ISBNValidator.getInstance(false); - - assertThat(isbn10NoSep).hasSize(10); - assertIsValidISBN10(isbn10NoSep, isbnValidator); - assertThat(isbn10Sep).hasSize(13); - assertIsValidISBN10(isbn10Sep, isbnValidator); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "code#isbn10(boolean)", params = "true", repeat = 100) + void isbn10WithSeparator(String isbn10) { + assertThat(isbn10).hasSize(13); + assertIsValidISBN10(isbn10); } - @RepeatedTest(100) - void testIsbn13() { - final BaseFaker faker = new BaseFaker(); - final String isbn13NoSep = faker.code().isbn13(false); - final String isbn13Sep = faker.code().isbn13(true); - final ISBNValidator isbnValidator = ISBNValidator.getInstance(false); - - assertThat(isbn13NoSep).hasSize(13); - assertIsValidISBN13(isbn13NoSep, isbnValidator); - - assertThat(isbn13Sep).hasSize(17); - assertIsValidISBN13(isbn13Sep, isbnValidator); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "code#isbn13", repeat = 100) + void isbn13DefaultIsNoSeparator(String isbn13) { + assertIsValidISBN13(isbn13); + assertThat(isbn13).doesNotContain("-"); } - private void assertIsValidISBN10(String isbn10, ISBNValidator isbnValidator) { - assertThat(isbnValidator.isValidISBN10(isbn10)).describedAs(isbn10 + " is valid").isTrue(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "code#isbn13(boolean)", params = "false", repeat = 100) + void isbn13WithoutSeparator(String isbn13) { + assertThat(isbn13).hasSize(13); + assertIsValidISBN13(isbn13); } - private void assertIsValidISBN13(String isbn13, ISBNValidator isbnValidator) { - assertThat(isbnValidator.isValidISBN13(isbn13)).describedAs(isbn13 + " is valid").isTrue(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "code#isbn13(boolean)", params = "true", repeat = 100) + void isbn13WithSeparator(String isbn13) { + assertThat(isbn13).hasSize(17); + assertIsValidISBN13(isbn13); } @RepeatedTest(100) void testOverrides() { - BaseFaker faker = new BaseFaker(new Locale("test")); - - final String isbn10Sep = faker.code().isbn10(true); - final String isbn13Sep = faker.code().isbn13(true); - - assertThat(isbn10Sep).matches("9971-\\d-\\d{4}-(\\d|X)"); - - assertThat(isbn13Sep).matches("(333|444)-9971-\\d-\\d{4}-\\d"); + // Requires a specific Locale("test") instance – not expressible via @FakerSource locale + BaseFaker localFaker = new BaseFaker(new Locale("test")); + assertThat(localFaker.code().isbn10(true)).matches("9971-\\d-\\d{4}-(\\d|X)"); + assertThat(localFaker.code().isbn13(true)).matches("(333|444)-9971-\\d-\\d{4}-\\d"); } @Test void asin() { - assertThat(faker.code().asin()).matches("B000([A-Z]|\\d){6}"); + assertThat(code.asin()).matches("B000([A-Z]|\\d){6}"); } @Test void imei() { - String imei = faker.code().imei(); - + String imei = code.imei(); assertThat(imei).matches("\\A[\\d.:\\-\\s]+\\z"); assertThat(LuhnCheckDigit.LUHN_CHECK_DIGIT.isValid(imei)).isTrue(); } @Test void ean8() { - assertThat(faker.code().ean8()).matches("\\d{8}"); + assertThat(code.ean8()).matches("\\d{8}"); } @Test void gtin8() { - assertThat(faker.code().gtin8()).matches("\\d{8}"); + assertThat(code.gtin8()).matches("\\d{8}"); } @Test void ean13() { - String ean13 = faker.code().ean13(); + String ean13 = code.ean13(); assertThat(ean13).matches("\\d{13}"); assertThat(EAN13CheckDigit.EAN13_CHECK_DIGIT.isValid(ean13)).isTrue(); } @Test void gtin13() { - String gtin13 = faker.code().gtin13(); + String gtin13 = code.gtin13(); assertThat(gtin13).matches("\\d{13}"); assertThat(EAN13CheckDigit.EAN13_CHECK_DIGIT.isValid(gtin13)).isTrue(); } @Test void isbnGs1() { - String isbnGs1 = faker.code().isbnGs1(); - assertThat(isbnGs1).matches("978|979"); + assertThat(code.isbnGs1()).matches("978|979"); } @Test void isbnGroup() { - String isbnGroup = faker.code().isbnGroup(); - assertThat(isbnGroup).matches("[01]"); + assertThat(code.isbnGroup()).matches("[01]"); } - @RepeatedTest(100) - void isbnRegistrant() { - String isbnRegistrant = faker.code().isbnRegistrant(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "code#isbnRegistrant", repeat = 100) + void isbnRegistrant(String isbnRegistrant) { assertThat(isbnRegistrant).matches("[0-9]{1,7}-[0-9]{1,6}"); } + + private void assertIsValidISBN10(String isbn10) { + assertThat(ISBN_VALIDATOR.isValidISBN10(isbn10)) + .describedAs("%s should be a valid ISBN-10", isbn10) + .isTrue(); + } + + private void assertIsValidISBN13(String isbn13) { + assertThat(ISBN_VALIDATOR.isValidISBN13(isbn13)) + .describedAs("%s should be a valid ISBN-13", isbn13) + .isTrue(); + } } + diff --git a/src/test/java/net/datafaker/providers/base/FinanceTest.java b/src/test/java/net/datafaker/providers/base/FinanceTest.java index fc4f9a31d..371d1825d 100644 --- a/src/test/java/net/datafaker/providers/base/FinanceTest.java +++ b/src/test/java/net/datafaker/providers/base/FinanceTest.java @@ -7,12 +7,12 @@ import de.speedbanking.iban.Iban; import de.speedbanking.iban.IbanRegistry; +import net.datafaker.junit.FakerSource; +import net.datafaker.junit.FakerSource.Param; import net.datafaker.providers.base.Finance.CreditCardType; import org.apache.commons.validator.routines.CreditCardValidator; -import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; import java.util.Collection; import java.util.List; @@ -25,21 +25,22 @@ class FinanceTest extends BaseFakerTest { private final Finance finance = faker.finance(); - @RepeatedTest(100) - void creditCard() { - assertThatCreditCardIsValid(finance.creditCard().replace("-", ""), null, CREDIT_CARD_VALIDATOR); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "finance#creditCard()", repeat = 100) + void creditCard(String creditCard) { + assertThatCreditCardIsValid(creditCard.replace("-", ""), null, CREDIT_CARD_VALIDATOR); } - @RepeatedTest(10) - @SuppressWarnings("removal") - void nasdaqTicker() { - assertThat(finance.nasdaqTicker()).matches("[A-Z.-]+"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "finance#nasdaqTicker", repeat = 10) + void nasdaqTicker(CharSequence ticker) { + assertThat(ticker).matches("[A-Z.-]+"); } - @RepeatedTest(10) - @SuppressWarnings("removal") - void nyseTicker() { - assertThat(finance.nyseTicker()).matches("[A-Z.-]+"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "finance#nyseTicker", repeat = 10) + void nyseTicker(String ticker) { + assertThat(ticker).matches("[A-Z.-]+"); } @Override @@ -48,25 +49,25 @@ protected Collection providerListTest() { return List.of(TestSpec.of(finance::stockMarket, "finance.stock_market")); } - @RepeatedTest(100) - void bic() { - assertThatBicIsValid(finance.bic()); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "finance#bic", repeat = 100) + void bic(String bic) { + assertThatBicIsValid(bic); } - @RepeatedTest(100) - void iban() { - String iban = finance.iban(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "finance#iban", repeat = 100) + void iban(String iban) { assertThatIbanIsValid(iban); assertThat(Finance.ibanSupportedCountries()).contains(Iban.of(iban).getCountryCode()); } - @Test - void ibanWithCountryCode() { - String ibanDe = finance.iban("DE"); - assertThatIban(ibanDe) - .hasCountryCode("DE") - .hasLength(IbanRegistry.DE.getIbanLength()) - .matches("DE\\d{20}"); + @ParameterizedTest(name = "[{index}] {1}: {0}") + @FakerSource(code = "finance#iban(String)", multiParams = @Param({"IR", "LB", "PL"}), repeat = 5) + void ibanWithCountryCode(CharSequence iban, String countryCode) { + assertThatIban(iban.toString()) + .hasCountryCode(countryCode) + .hasLength(IbanRegistry.getByCode(countryCode).getIbanLength()); } @Test @@ -89,11 +90,10 @@ void ibanWithAllCountryCodes() { } } - @ParameterizedTest(name = "[{index}] {0}") - @EnumSource(CreditCardType.class) - void creditCardWithType(CreditCardType type) { - String cc = finance.creditCard(type).replace("-", ""); - assertThatCreditCardIsValid(cc, type, CREDIT_CARD_VALIDATOR); + @ParameterizedTest(name = "[{index}] {1}: {0}") + @FakerSource(code = "finance#creditCard(CreditCardType)", repeat = 10) + void creditCardWithType(String creditCard, CreditCardType type) { + assertThatCreditCardIsValid(creditCard.replace("-", ""), type, CREDIT_CARD_VALIDATOR); } @Test @@ -106,33 +106,36 @@ void costaRicaIbanMustBeValid() { .hasLength(IbanRegistry.CR.getIbanLength()); } - @RepeatedTest(100) - void visaCard() { - String creditCard = finance.creditCard(CreditCardType.VISA).replace("-", ""); - assertThat(creditCard).startsWith("4").hasSize(16); - assertThatCreditCardIsValid(creditCard, CreditCardType.VISA, CREDIT_CARD_VALIDATOR); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "finance#creditCard(CreditCardType)", params = "VISA", repeat = 100) + void visaCard(String creditCard) { + String digits = creditCard.replace("-", ""); + assertThat(digits).startsWith("4").hasSize(16); + assertThatCreditCardIsValid(digits, CreditCardType.VISA, CREDIT_CARD_VALIDATOR); } - @RepeatedTest(100) - void discoverCard() { - String creditCard = finance.creditCard(CreditCardType.DISCOVER).replace("-", ""); - assertThat(creditCard).startsWith("6").hasSize(16); - assertThatCreditCardIsValid(creditCard, CreditCardType.DISCOVER, CREDIT_CARD_VALIDATOR); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "finance#creditCard(CreditCardType)", params = "DISCOVER", repeat = 100) + void discoverCard(String creditCard) { + String digits = creditCard.replace("-", ""); + assertThat(digits).startsWith("6").hasSize(16); + assertThatCreditCardIsValid(digits, CreditCardType.DISCOVER, CREDIT_CARD_VALIDATOR); } - @RepeatedTest(100) - void unionpayCard() { + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "finance#creditCard(CreditCardType)", params = "UNIONPAY", repeat = 100) + void unionpayCard(String creditCard) { List startingDigits = List.of("62", "64", "65", "81"); - String creditCard = finance.creditCard(CreditCardType.UNIONPAY).replace("-", ""); - assertThatCreditCardIsValid(creditCard, CreditCardType.UNIONPAY, CreditCardValidator.genericCreditCardValidator(16)); + String digits = creditCard.replace("-", ""); + assertThatCreditCardIsValid(digits, CreditCardType.UNIONPAY, CreditCardValidator.genericCreditCardValidator(16)); assertThat(startingDigits) - .as("UnionPay card '%s' should start with one of %s", creditCard, startingDigits) - .anyMatch(prefix -> creditCard.startsWith(prefix)); + .as("UnionPay card '%s' should start with one of %s", digits, startingDigits) + .anyMatch(prefix -> digits.startsWith(prefix)); } - @RepeatedTest(100) - void usRoutingNumber() { - String rtn = finance.usRoutingNumber(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "finance#usRoutingNumber", repeat = 100) + void usRoutingNumber(String rtn) { assertThat(rtn).matches("\\d{9}"); int check = 0; for (int index = 0; index < 3; index++) { @@ -148,15 +151,16 @@ void usRoutingNumber() { * Asserts that the given credit card number is valid according to the specified validator. * * @param creditCard the card number to validate - * @param type the expected card type, or {@code null} if unspecified + * @param creditCardType the expected card type, or {@code null} if unspecified * @param creditCardValidator the validator instance used for verification */ - private void assertThatCreditCardIsValid(String creditCard, CreditCardType type, CreditCardValidator creditCardValidator) { + private void assertThatCreditCardIsValid(String creditCard, CreditCardType creditCardType, CreditCardValidator creditCardValidator) { + assertThat(creditCardValidator).isNotNull(); assertThat(creditCard) .satisfies(cc -> { assertThat(creditCardValidator.isValid(cc)) .as("Credit card %s (%s) should be valid according to %s", - cc, Objects.requireNonNullElse(type, "unspecified"), creditCardValidator.getClass().getName()) + cc, Objects.requireNonNullElse(creditCardType, "unspecified"), creditCardValidator.getClass().getName()) .isTrue(); }); } diff --git a/src/test/java/net/datafaker/providers/base/HttpTest.java b/src/test/java/net/datafaker/providers/base/HttpTest.java index a3c543e68..78ab00d41 100644 --- a/src/test/java/net/datafaker/providers/base/HttpTest.java +++ b/src/test/java/net/datafaker/providers/base/HttpTest.java @@ -1,6 +1,8 @@ package net.datafaker.providers.base; -import org.junit.jupiter.api.RepeatedTest; +import static org.assertj.core.api.Assertions.assertThat; + +import net.datafaker.junit.FakerSource; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -8,8 +10,6 @@ import java.util.Collection; import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; - class HttpTest extends BaseFakerTest { private final Http http = faker.http(); @@ -17,115 +17,125 @@ class HttpTest extends BaseFakerTest { @Override protected Collection providerListTest() { return List.of( - TestSpec.of(http::requestHeader, "http.request_header"), + TestSpec.of(http::requestHeader, "http.request_header"), TestSpec.of(http::responseHeader, "http.response_header"), - TestSpec.of(http::contentType, "http.content_type"), - TestSpec.of(http::httpMethod, "http.http_method"), - TestSpec.of(http::httpVersion, "http.http_version"), - TestSpec.of(http::encoding, "http.encoding"), - TestSpec.of(() -> http.responseBody("application/json"), "http.response_body.json"), - TestSpec.of(() -> http.responseBody("application/xml"), "http.response_body.xml"), - TestSpec.of(() -> http.responseBody("text/html"), "http.response_body.html"), - TestSpec.of(() -> http.responseBody("text/plain"), "http.response_body.plain"), - TestSpec.of(() -> http.responseBody("text/csv"), "http.response_body.csv"), + TestSpec.of(http::contentType, "http.content_type"), + TestSpec.of(http::httpMethod, "http.http_method"), + TestSpec.of(http::httpVersion, "http.http_version"), + TestSpec.of(http::encoding, "http.encoding"), + TestSpec.of(() -> http.responseBody("application/json"), "http.response_body.json"), + TestSpec.of(() -> http.responseBody("application/xml"), "http.response_body.xml"), + TestSpec.of(() -> http.responseBody("text/html"), "http.response_body.html"), + TestSpec.of(() -> http.responseBody("text/plain"), "http.response_body.plain"), + TestSpec.of(() -> http.responseBody("text/csv"), "http.response_body.csv"), TestSpec.of(() -> http.responseBody("application/javascript"), "http.response_body.javascript"), - TestSpec.of(() -> http.responseBody("text/css"), "http.response_body.css"), - TestSpec.of(() -> http.responseBody("application/graphql"), "http.response_body.graphql"), - TestSpec.of(() -> http.responseBody("text/markdown"), "http.response_body.markdown") + TestSpec.of(() -> http.responseBody("text/css"), "http.response_body.css"), + TestSpec.of(() -> http.responseBody("application/graphql"), "http.response_body.graphql"), + TestSpec.of(() -> http.responseBody("text/markdown"), "http.response_body.markdown") ); } - @RepeatedTest(20) - void statusCode() { - assertThat(http.statusCode()).isBetween(100, 599); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#statusCode", repeat = 20) + void statusCode(int code) { + assertThat(code).isBetween(100, 599); } - @RepeatedTest(10) - void informational() { - assertThat(http.informational()).isBetween(100, 199); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#informational", repeat = 10) + void informational(int code) { + assertThat(code).isBetween(100, 199); } - @RepeatedTest(10) - void successful() { - assertThat(http.successful()).isBetween(200, 299); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#successful", repeat = 10) + void successful(int code) { + assertThat(code).isBetween(200, 299); } - @RepeatedTest(10) - void redirect() { - assertThat(http.redirect()).isBetween(300, 399); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#redirect", repeat = 10) + void redirect(int code) { + assertThat(code).isBetween(300, 399); } - @RepeatedTest(10) - void clientError() { - assertThat(http.clientError()).isBetween(400, 499); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#clientError", repeat = 10) + void clientError(int code) { + assertThat(code).isBetween(400, 499); } - @RepeatedTest(10) - void serverError() { - assertThat(http.serverError()).isBetween(500, 599); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#serverError", repeat = 10) + void serverError(int code) { + assertThat(code).isBetween(500, 599); } - @RepeatedTest(10) - void statusMessage() { - assertThat(http.statusMessage()).isNotBlank(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#statusMessage", repeat = 10) + void statusMessage(String msg) { + assertThat(msg).isNotBlank(); } - @RepeatedTest(10) - void informationalMessage() { - assertThat(http.informationalMessage()).isNotBlank(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#informationalMessage", repeat = 10) + void informationalMessage(String msg) { + assertThat(msg).isNotBlank(); } - @RepeatedTest(10) - void successfulMessage() { - assertThat(http.successfulMessage()).isNotBlank(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#successfulMessage", repeat = 10) + void successfulMessage(String msg) { + assertThat(msg).isNotBlank(); } - @RepeatedTest(10) - void redirectMessage() { - assertThat(http.redirectMessage()).isNotBlank(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#redirectMessage", repeat = 10) + void redirectMessage(String msg) { + assertThat(msg).isNotBlank(); } - @RepeatedTest(10) - void clientErrorMessage() { - assertThat(http.clientErrorMessage()).isNotBlank(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#clientErrorMessage", repeat = 10) + void clientErrorMessage(String msg) { + assertThat(msg).isNotBlank(); } - @RepeatedTest(10) - void serverErrorMessage() { - assertThat(http.serverErrorMessage()).isNotBlank(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#serverErrorMessage", repeat = 10) + void serverErrorMessage(String msg) { + assertThat(msg).isNotBlank(); } - @RepeatedTest(10) - void statusCodeWithReason() { - String entry = http.statusCodeWithReason(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#statusCodeWithReason", repeat = 10) + void statusCodeWithReason(String entry) { assertThat(entry).matches("\\d{3} .+"); - int code = Integer.parseInt(entry.substring(0, 3)); - assertThat(code).isBetween(100, 599); + assertThat(Integer.parseInt(entry.substring(0, 3))).isBetween(100, 599); } - @RepeatedTest(10) - void userAgent() { - assertThat(http.userAgent()).isNotBlank().startsWith("Mozilla/"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#userAgent", repeat = 10) + void userAgent(String ua) { + assertThat(ua).isNotBlank().startsWith("Mozilla/"); } - @RepeatedTest(10) - void mobileUserAgent() { - assertThat(http.mobileUserAgent()).isNotBlank().contains("Mobile"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#mobileUserAgent", repeat = 10) + void mobileUserAgent(String ua) { + assertThat(ua).isNotBlank().contains("Mobile"); } - @Test - void userAgentForAllBrowsers() { - for (var browser : Http.Browser.values()) { - assertThat(http.userAgent(browser)) - .as("User agent for browser %s", browser) - .isNotBlank() - .startsWith("Mozilla/"); - } + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#userAgent(Http.Browser)", repeat = 1) + void userAgentForAllBrowsers(String ua) { + assertThat(ua).isNotBlank().startsWith("Mozilla/"); } - @RepeatedTest(20) - void responseBodyNoArg() { - assertThat(http.responseBody()).isNotBlank(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "http#responseBody", repeat = 20) + void responseBodyNoArg(String body) { + assertThat(body).isNotBlank(); } @ParameterizedTest @@ -156,7 +166,8 @@ void responseBodyForContentType(String contentType, String expectedPrefix) { @Test void responseBodyUnknownContentTypeFallsBackToJson() { - String body = http.responseBody("application/unknown"); - assertThat(body).isNotBlank().startsWith("{"); + assertThat(http.responseBody("application/unknown")).isNotBlank().startsWith("{"); } + } + diff --git a/src/test/java/net/datafaker/providers/base/IdNumberTest.java b/src/test/java/net/datafaker/providers/base/IdNumberTest.java index fe6f3e900..7b9c4f930 100644 --- a/src/test/java/net/datafaker/providers/base/IdNumberTest.java +++ b/src/test/java/net/datafaker/providers/base/IdNumberTest.java @@ -1,12 +1,21 @@ package net.datafaker.providers.base; +import static net.datafaker.idnumbers.pt.br.IdNumberGeneratorPtBrUtil.isCPFValid; +import static net.datafaker.providers.base.IdNumber.GenderRequest.ANY; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; + +import static java.lang.Integer.parseInt; +import static java.time.format.DateTimeFormatter.ofPattern; + import net.datafaker.Faker; import net.datafaker.helpers.IdNumberPatterns; import net.datafaker.idnumbers.SouthAfricanIdNumber; import net.datafaker.idnumbers.SwedenIdNumber; +import net.datafaker.junit.FakerSource; import net.datafaker.providers.base.IdNumber.IdNumberRequest; import org.assertj.core.api.AbstractStringAssert; -import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; @@ -14,34 +23,12 @@ import java.time.LocalDate; import java.util.Locale; - -import static java.lang.Integer.parseInt; -import static java.time.format.DateTimeFormatter.ofPattern; -import static net.datafaker.idnumbers.pt.br.IdNumberGeneratorPtBrUtil.isCPFValid; -import static net.datafaker.providers.base.IdNumber.GenderRequest.ANY; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; +import java.util.regex.Pattern; @TestInstance(PER_CLASS) class IdNumberTest { - private final Faker faker = new Faker(); - private static final Faker SWEDISH = new Faker(new Locale("sv", "SE")); - private static final Faker SOUTH_AFRICA = new Faker(new Locale("en", "ZA")); - private static final Faker US = new Faker(new Locale("en", "US")); - private static final Faker ESTONIAN = new Faker(new Locale("et", "EE")); - private static final Faker LATVIAN = new Faker(new Locale("lv", "LV")); - private static final Faker ALBANIAN = new Faker(new Locale("sq", "AL")); - private static final Faker BULGARIAN = new Faker(new Locale("bg", "BG")); - private static final Faker MACEDONIAN = new Faker(new Locale("mk", "MK")); - private static final Faker ROMANIAN = new Faker(new Locale("ro", "RO")); - private static final Faker UKRAINIAN = new Faker(new Locale("uk", "UA")); - private static final Faker FRENCH = new Faker(new Locale("fr", "FR")); - private static final Faker ITALIAN = new Faker(new Locale("it", "IT")); - private static final Faker HUNGARIAN = new Faker(new Locale("hu", "HU")); - private static final Faker BRAZILIAN = new Faker(new Locale("pt", "BR")); - private static final Faker NORWEGIAN = new Faker(new Locale("no", "NO")); + private final Faker faker = new Faker(); @Test void testValid() { @@ -53,129 +40,144 @@ void testInvalid() { assertThat(faker.idNumber().invalid()).matches("[0-9]\\d{2}-\\d{2}-\\d{4}"); } - @RepeatedTest(100) - void testSsnValid() { - assertThat(faker.idNumber().ssnValid()).matches("[0-8]\\d{2}-\\d{2}-\\d{4}"); - assertThat(US.idNumber().valid()).matches("[0-8]\\d{2}-\\d{2}-\\d{4}"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#ssnValid", repeat = 100) + void testSsnValid(String validSsn) { + assertThat(validSsn).matches("[0-8]\\d{2}-\\d{2}-\\d{4}"); + } + + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "en-US", repeat = 100) + void usIdNumber(String idNumber) { + assertThat(idNumber).matches("[0-8]\\d{2}-\\d{2}-\\d{4}"); } - @RepeatedTest(100) - void testSsnInvalid() { - String invalidSsn = US.idNumber().invalid(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#invalid", locale = "en-US", repeat = 100) + void testSsnInvalid(String invalidSsn) { if (!invalidSsn.startsWith("9")) { assertThat(invalidSsn).matches("[0-8]\\d{2}-\\d{2}-\\d{4}"); } } - @RepeatedTest(100) - void testValidSwedishSsn() { - String actual = SWEDISH.idNumber().valid(); - assertThat(actual).matches(IdNumberPatterns.SWEDISH); - assertThat(SwedenIdNumber.isValidSwedishSsn(actual)).isTrue(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#singaporeanFin", repeat = 100) + void testSingaporeanFin(String id) { + assertThat(id).matches("G[0-9]{7}[A-Z]"); } - @RepeatedTest(100) - void testInvalidSwedishSsn() { - String actual = SWEDISH.idNumber().invalid(); - assertThat(actual).matches(IdNumberPatterns.SWEDISH); - assertThat(SwedenIdNumber.isValidSwedishSsn(actual)).isFalse(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#singaporeanFinBefore2000", repeat = 100) + void testSingaporeanFinBefore2000(String id) { + assertThat(id).matches("F[0-9]{7}[A-Z]"); } - @RepeatedTest(100) - void southAfrica_valid() { - String actual = SOUTH_AFRICA.idNumber().valid(); - assertThat(actual).matches(IdNumberPatterns.SOUTH_AFRICAN); - assertThat(SouthAfricanIdNumber.isValidEnZASsn(actual)).isTrue(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#singaporeanUin", repeat = 100) + void testSingaporeanUin(String id) { + assertThat(id).matches("T[0-9]{7}[A-Z]"); } - @RepeatedTest(100) - void southAfrica_invalid() { - assertThat(SOUTH_AFRICA.idNumber().invalid()).matches(IdNumberPatterns.SOUTH_AFRICAN); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#singaporeanUinBefore2000", repeat = 100) + void testSingaporeanUinBefore2000(String id) { + assertThat(id).matches("S[0-9]{7}[A-Z]"); } - @RepeatedTest(100) - void testSingaporeanFin() { - assertThat(faker.idNumber().singaporeanFin()).matches("G[0-9]{7}[A-Z]"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#peselNumber", repeat = 100) + void testPeselNumber(String id) { + assertThat(id).matches("[0-9]{11}"); } - @RepeatedTest(100) - void testSingaporeanFinBefore2000() { - assertThat(faker.idNumber().singaporeanFinBefore2000()).matches("F[0-9]{7}[A-Z]"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "sv-SE", repeat = 100) + void testValidSwedishSsn(String actual) { + assertThat(actual).matches(IdNumberPatterns.SWEDISH); + assertThat(SwedenIdNumber.isValidSwedishSsn(actual)).isTrue(); } - @RepeatedTest(100) - void testSingaporeanUin() { - assertThat(faker.idNumber().singaporeanUin()).matches("T[0-9]{7}[A-Z]"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#invalid", locale = "sv-SE", repeat = 100) + void testInvalidSwedishSsn(String actual) { + assertThat(actual).matches(IdNumberPatterns.SWEDISH); + assertThat(SwedenIdNumber.isValidSwedishSsn(actual)).isFalse(); } - @RepeatedTest(100) - void testSingaporeanUinBefore2000() { - assertThat(faker.idNumber().singaporeanUinBefore2000()).matches("S[0-9]{7}[A-Z]"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "en-ZA", repeat = 100) + void southAfrica_valid(String actual) { + assertThat(actual).matches(IdNumberPatterns.SOUTH_AFRICAN); + assertThat(SouthAfricanIdNumber.isValidEnZASsn(actual)).isTrue(); } - @RepeatedTest(100) - @SuppressWarnings("deprecation") - void testPeselNumber() { - assertThat(faker.idNumber().peselNumber()).matches("[0-9]{11}"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#invalid", locale = "en-ZA", repeat = 100) + void southAfrica_invalid(String actual) { + assertThat(actual).matches(IdNumberPatterns.SOUTH_AFRICAN); } - @RepeatedTest(100) - void estonianPersonalCode_valid() { - assertThatPin(ESTONIAN.idNumber().valid()).matches(IdNumberPatterns.ESTONIAN); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "et-EE", repeat = 100) + void estonianPersonalCode_valid(String pin) { + assertThatPinMatches(pin, IdNumberPatterns.ESTONIAN); } - @RepeatedTest(100) - void estonianPersonalCode_invalid() { - assertThatPin(ESTONIAN.idNumber().invalid()).matches(IdNumberPatterns.ESTONIAN); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#invalid", locale = "et-EE", repeat = 100) + void estonianPersonalCode_invalid(String pin) { + assertThatPinMatches(pin, IdNumberPatterns.ESTONIAN); } - @RepeatedTest(100) - void brazilianPersonalCode_valid() { - String actual = BRAZILIAN.idNumber().valid(); - assertThatPin(actual).matches(IdNumberPatterns.BRAZILIAN); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "pt-BR", repeat = 100) + void brazilianPersonalCode_valid(String actual) { + assertThatPinMatches(actual, IdNumberPatterns.BRAZILIAN); assertThat(isCPFValid(actual)).describedAs(() -> "Current value " + actual).isTrue(); } - @RepeatedTest(100) - void brazilianPersonalCode_invalid() { - String actual = BRAZILIAN.idNumber().invalid(); - assertThatPin(actual).matches(IdNumberPatterns.BRAZILIAN); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#invalid", locale = "pt-BR", repeat = 100) + void brazilianPersonalCode_invalid(String actual) { + assertThatPinMatches(actual, IdNumberPatterns.BRAZILIAN); assertThat(isCPFValid(actual)).describedAs(() -> "Current value " + actual).isFalse(); } - @RepeatedTest(100) - @SuppressWarnings("ResultOfMethodCallIgnored") - void latvianPersonalCode_valid_oldFormat_before_2017_07_01() { - int minAgeApplicableForOldFormat = LocalDate.now().getYear() - 2017 + 1; - String idNumber = LATVIAN.idNumber().valid(new IdNumberRequest(minAgeApplicableForOldFormat, minAgeApplicableForOldFormat + 50, ANY)).idNumber(); - assertThatPin(idNumber).matches("[0-3]\\d[0-1]\\d{3}-[0-2]\\d{4}"); - assertThatCode(() -> LocalDate.parse(idNumber.substring(0, 6), ofPattern("ddMMyy"))).doesNotThrowAnyException(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber", locale = "lv-LV", repeat = 100) + void latvianPersonalCode_valid_oldFormat_before_2017_07_01(IdNumber idNumber) { + int minAge = LocalDate.now().getYear() - 2017 + 1; + String idn = idNumber.valid(new IdNumberRequest(minAge, minAge + 50, ANY)).idNumber(); + assertThatPinMatches(idn, "[0-3]\\d[0-1]\\d{3}-[0-2]\\d{4}"); + assertThatCode(() -> LocalDate.parse(idn.substring(0, 6), ofPattern("ddMMyy"))).doesNotThrowAnyException(); } - @RepeatedTest(100) - void latvianPersonalCode_valid_newFormat_since_2017_07_01() { - int maxAgeApplicableForNewFormat = LocalDate.now().getYear() - 2017 - 1; - assertThatPin(LATVIAN.idNumber().valid(new IdNumberRequest(0, maxAgeApplicableForNewFormat, ANY)).idNumber()).matches("3[2-9]\\d{9}"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber", locale = "lv-LV", repeat = 100) + void latvianPersonalCode_valid_newFormat_since_2017_07_01(IdNumber idNumber) { + int maxAge = LocalDate.now().getYear() - 2017 - 1; + assertThatPin(idNumber.valid(new IdNumberRequest(0, maxAge, ANY)).idNumber()).matches("3[2-9]\\d{9}"); } - @RepeatedTest(100) - void latvianPersonalCode_invalid() { - assertThatPin(LATVIAN.idNumber().invalid()).matches("[0-3]\\d[0-1]\\d{3}-[0-2]\\d{4}"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#invalid", locale = "lv-LV", repeat = 100) + void latvianPersonalCode_invalid(String pin) { + assertThatPinMatches(pin, "[0-3]\\d[0-1]\\d{3}-[0-2]\\d{4}"); } - @RepeatedTest(100) - void albanianPersonalCode_valid() { - String pin = ALBANIAN.idNumber().valid(); - assertThatPin(pin).matches("\\w\\d{8}\\w"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "sq-AL", repeat = 100) + void albanianPersonalCode_valid(String pin) { + assertThatPinMatches(pin, "\\w\\d{8}\\w"); assertThat(parseInt(pin.substring(2, 4)) % 50) .as(() -> "Valid PIN %s should have month number between 1..12 (for males) or 51..62 (for females)".formatted(pin)) .isBetween(1, 12); } - @RepeatedTest(100) - void albanianPersonalCode_invalid() { - String pin = ALBANIAN.idNumber().invalid(); - assertThatPin(pin).matches("\\w\\d{8}\\w"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#invalid", locale = "sq-AL", repeat = 100) + void albanianPersonalCode_invalid(String pin) { + assertThatPinMatches(pin, "\\w\\d{8}\\w"); assertThat(parseInt(pin.substring(2, 4))) .as(() -> "Invalid PIN %s should have month greater than (any month + 50)".formatted(pin)) .isGreaterThan(62); @@ -184,67 +186,67 @@ void albanianPersonalCode_invalid() { @ParameterizedTest @ValueSource(strings = {"en", "ro", "ru"}) void moldovaPersonalCode_valid(String language) { - final var faker = new Faker(new Locale(language, "MD")); + final var localFaker = new Faker(new Locale(language, "MD")); for (int i = 0; i < 100; i++) { - String pin = faker.idNumber().valid(); - assertThatPin(pin).matches("\\d{13}"); + assertThatPin(localFaker.idNumber().valid()).matches("\\d{13}"); } } @ParameterizedTest @ValueSource(strings = {"en", "ro", "ru"}) void moldovaPersonalCode_invalid(String language) { - final var faker = new Faker(new Locale(language, "MD")); + final var localFaker = new Faker(new Locale(language, "MD")); for (int i = 0; i < 100; i++) { - String pin = faker.idNumber().invalid(); - assertThatPin(pin).matches("\\d{13}"); + assertThatPin(localFaker.idNumber().invalid()).matches("\\d{13}"); } } - @RepeatedTest(100) - void bulgarianPersonalCode_valid() { - String pin = BULGARIAN.idNumber().valid(); - assertThatPin(pin).matches("\\d{10}"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "bg-BG", repeat = 100) + void bulgarianPersonalCode_valid(String pin) { + assertThatPinMatches(pin, "\\d{10}"); } - @RepeatedTest(100) - void bulgarianPersonalCode_invalid() { - String pin = BULGARIAN.idNumber().invalid(); - assertThatPin(pin).matches("\\d{10}"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#invalid", locale = "bg-BG", repeat = 100) + void bulgarianPersonalCode_invalid(String pin) { + assertThatPinMatches(pin, "\\d{10}"); } - @RepeatedTest(100) - void macedonianPersonalCode_valid() { - String pin = MACEDONIAN.idNumber().valid(); - assertThatPin(pin).matches("\\d{13}"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "mk-MK", repeat = 100) + void macedonianPersonalCode_valid(String pin) { + assertThatPinMatches(pin, "\\d{13}"); } - @RepeatedTest(100) - void romanianPersonalCode_valid() { - String pin = ROMANIAN.idNumber().valid(); - assertThatPin(pin).matches("\\d{13}"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#invalid", locale = "mk-MK", repeat = 100) + void macedonianPersonalCode_invalid(String pin) { + assertThatPinMatches(pin, "\\d{13}"); } - @RepeatedTest(100) - void macedonianPersonalCode_invalid() { - String pin = MACEDONIAN.idNumber().invalid(); - assertThatPin(pin).matches("\\d{13}"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "ro-RO", repeat = 100) + void romanianPersonalCode_valid(String pin) { + assertThatPinMatches(pin, "\\d{13}"); } - @RepeatedTest(100) - void ukrainianUznr_valid() { - assertThatPin(UKRAINIAN.idNumber().valid()).matches(IdNumberPatterns.UKRAINIAN); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "uk-UA", repeat = 100) + void ukrainianUznr_valid(String pin) { + assertThatPinMatches(pin, IdNumberPatterns.UKRAINIAN); } - @RepeatedTest(100) - void ukrainianUznr_invalid() { - assertThatPin(UKRAINIAN.idNumber().invalid()).matches(IdNumberPatterns.UKRAINIAN); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#invalid", locale = "uk-UA", repeat = 100) + void ukrainianUznr_invalid(String pin) { + assertThatPinMatches(pin, IdNumberPatterns.UKRAINIAN); } - @Test - void frenchIdNumber() { - String actual = FRENCH.idNumber().valid(); - assertThat(actual).matches(IdNumberPatterns.FRENCH); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "fr-FR", repeat = 10) + void frenchIdNumber(String idNumber) { + assertThat(idNumber).matches(IdNumberPatterns.FRENCH); } @Test @@ -253,33 +255,47 @@ void italianIdNumberSample() { assertThatPin("BBBTTT20H12X122H").matches(IdNumberPatterns.ITALIAN); } - @RepeatedTest(100) - void italianIdNumber() { - assertThatPin(ITALIAN.idNumber().valid()).matches(IdNumberPatterns.ITALIAN); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "it-IT", repeat = 100) + void italianIdNumber(String pin) { + assertThatPinMatches(pin, IdNumberPatterns.ITALIAN); } - @RepeatedTest(100) - void hungarianIdNumber_valid() { - assertThatPin(HUNGARIAN.idNumber().valid()).matches(IdNumberPatterns.HUNGARIAN); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "hu-HU", repeat = 100) + void hungarianIdNumber_valid(String pin) { + assertThatPinMatches(pin, IdNumberPatterns.HUNGARIAN); } - @RepeatedTest(100) - void hungarianIdNumber_invalid() { - assertThatPin(HUNGARIAN.idNumber().invalid()).matches(IdNumberPatterns.HUNGARIAN); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#invalid", locale = "hu-HU", repeat = 100) + void hungarianIdNumber_invalid(String pin) { + assertThatPinMatches(pin, IdNumberPatterns.HUNGARIAN); } - @RepeatedTest(100) - void norwegianIdNumber_valid() { - assertThatPin(NORWEGIAN.idNumber().valid()).matches(IdNumberPatterns.NORWEGIAN); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#valid", locale = "no-NO", repeat = 100) + void norwegianIdNumber_valid(String pin) { + assertThatPinMatches(pin, IdNumberPatterns.NORWEGIAN); } - @RepeatedTest(100) - void norwegianIdNumber_invalid() { - assertThatPin(NORWEGIAN.idNumber().invalid()).matches(IdNumberPatterns.NORWEGIAN); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "idNumber#invalid", locale = "no-NO", repeat = 100) + void norwegianIdNumber_invalid(String pin) { + assertThatPinMatches(pin, IdNumberPatterns.NORWEGIAN); } + @Deprecated private static AbstractStringAssert assertThatPin(String pin) { - return assertThat(pin) - .as(() -> "PIN: %s".formatted(pin)); + return assertThat(pin).as(() -> "PIN: %s".formatted(pin)); } + + private static void assertThatPinMatches(String pin, String regex) { + assertThatPinMatches(pin, Pattern.compile(regex)); + } + + private static void assertThatPinMatches(String pin, Pattern pattern) { + assertThat(pin).as(() -> "PIN: %s".formatted(pin)).matches(pattern); + } + } diff --git a/src/test/java/net/datafaker/providers/base/InternetTest.java b/src/test/java/net/datafaker/providers/base/InternetTest.java index 13af48326..c1d4ce51c 100644 --- a/src/test/java/net/datafaker/providers/base/InternetTest.java +++ b/src/test/java/net/datafaker/providers/base/InternetTest.java @@ -1,6 +1,20 @@ package net.datafaker.providers.base; +import static net.datafaker.providers.base.Internet.PortRange.RegisteredPorts; +import static net.datafaker.providers.base.Internet.PortRange.WellKnownPorts; +import static org.assertj.core.api.Assertions.anyOf; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import static java.lang.Integer.parseInt; + import net.datafaker.Faker; +import net.datafaker.junit.FakerSource; import net.datafaker.providers.base.Internet.PortRange; import net.datafaker.service.FakeValuesService; import org.apache.commons.validator.routines.EmailValidator; @@ -21,32 +35,22 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static java.lang.Integer.parseInt; -import static net.datafaker.providers.base.Internet.PortRange.RegisteredPorts; -import static net.datafaker.providers.base.Internet.PortRange.WellKnownPorts; -import static org.assertj.core.api.Assertions.anyOf; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - class InternetTest { public static final Pattern IPV6_HOST_ADDRESS = Pattern.compile("[0-9a-fA-F]{1,4}(:([0-9a-fA-F]{1,4})){1,7}"); - private final Faker faker = new Faker(); + private final Faker faker = new Faker(); + private final Internet internet = faker.internet(); - @RepeatedTest(10) - @SuppressWarnings("removal") - void testUsername() { - assertThat(faker.internet().username()).matches("^(\\w+)\\.(\\w+)$"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "internet#username", repeat = 10) + void testUsername(String username) { + assertThat(username).matches("^(\\w+)\\.(\\w+)$"); } - @RepeatedTest(10) - void emailSubject() { - assertThat(faker.internet().emailSubject()).isNotBlank(); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "internet#emailSubject", repeat = 10) + void emailSubject(String emailSubject) { + assertThat(emailSubject).isNotBlank(); } @ParameterizedTest @@ -78,47 +82,47 @@ private static Stream userNameWithSpacesProvider() { @Test void testEmailAddress() { - String emailAddress = faker.internet().emailAddress(); + String emailAddress = internet.emailAddress(); assertThat(EmailValidator.getInstance().isValid(emailAddress)).isTrue(); } @Test @SuppressWarnings("removal") void testEmailAddressWithNameParameter() { - String username = faker.internet().username(); - String emailAddress = faker.internet().emailAddress(username); + String username = internet.username(); + String emailAddress = internet.emailAddress(username); assertThat(emailAddress).startsWith(username + "@"); assertThat(EmailValidator.getInstance().isValid(emailAddress)).isTrue(); - emailAddress = faker.internet().emailAddress("Hal"); + emailAddress = internet.emailAddress("Hal"); assertThat(emailAddress).startsWith("hal@"); assertThat(EmailValidator.getInstance().isValid(emailAddress)).isTrue(); - emailAddress = faker.internet().emailAddress("T-800"); + emailAddress = internet.emailAddress("T-800"); assertThat(emailAddress).startsWith("t800@"); assertThat(EmailValidator.getInstance().isValid(emailAddress)).isTrue(); - emailAddress = faker.internet().emailAddress("John McClane"); + emailAddress = internet.emailAddress("John McClane"); assertThat(emailAddress).startsWith("john.mcclane@"); assertThat(EmailValidator.getInstance().isValid(emailAddress)).isTrue(); - emailAddress = faker.internet().emailAddress("Tom Marvolo Riddle"); + emailAddress = internet.emailAddress("Tom Marvolo Riddle"); assertThat(emailAddress).startsWith("tom.riddle@"); assertThat(EmailValidator.getInstance().isValid(emailAddress)).isTrue(); - emailAddress = faker.internet().emailAddress("Dr. Emmett Brown"); + emailAddress = internet.emailAddress("Dr. Emmett Brown"); assertThat(emailAddress).startsWith("emmett.brown@"); assertThat(EmailValidator.getInstance().isValid(emailAddress)).isTrue(); - emailAddress = faker.internet().emailAddress("Dr. Henry Indiana Jones Jr."); + emailAddress = internet.emailAddress("Dr. Henry Indiana Jones Jr."); assertThat(emailAddress).startsWith("henry.jones@"); assertThat(EmailValidator.getInstance().isValid(emailAddress)).isTrue(); - emailAddress = faker.internet().emailAddress("Jeanne d'Arc"); + emailAddress = internet.emailAddress("Jeanne d'Arc"); assertThat(emailAddress).startsWith("jeanne.darc@"); assertThat(EmailValidator.getInstance().isValid(emailAddress)).isTrue(); - emailAddress = faker.internet().emailAddress("Jeanne d’Arc"); + emailAddress = internet.emailAddress("Jeanne d’Arc"); assertThat(emailAddress).startsWith("jeanne.darc@"); assertThat(EmailValidator.getInstance().isValid(emailAddress)).isTrue(); } @@ -155,11 +159,11 @@ void testEmailAddressTypeSafety() { void testSafeEmailAddress() { List emails = new ArrayList<>(); for (int i = 0; i < 100; i++) { - String emailAddress = faker.internet().safeEmailAddress(); + String emailAddress = internet.safeEmailAddress(); assertThat(EmailValidator.getInstance().isValid(emailAddress)).isTrue(); emails.add(emailAddress); } - final String safeDomain = faker.internet().resolve("internet.safe_email"); + final String safeDomain = internet.resolve("internet.safe_email"); assertThat(emails.stream().filter(t -> t.endsWith("@" + safeDomain)).collect(Collectors.toList())) .isNotEmpty(); @@ -169,12 +173,12 @@ void testSafeEmailAddress() { void testSafeEmailAddressWithNameParameter() { List emails = new ArrayList<>(); for (int i = 0; i < 100; i++) { - String emailAddress = faker.internet().safeEmailAddress("John McClane"); + String emailAddress = internet.safeEmailAddress("John McClane"); assertThat(emailAddress).startsWith("john.mcclane@"); assertThat(EmailValidator.getInstance().isValid(emailAddress)).isTrue(); emails.add(emailAddress); } - final String safeDomain = faker.internet().resolve("internet.safe_email"); + final String safeDomain = internet.resolve("internet.safe_email"); assertThat(emails.stream().filter(t -> t.endsWith("@" + safeDomain)).collect(Collectors.toList())) .isNotEmpty(); @@ -182,65 +186,65 @@ void testSafeEmailAddressWithNameParameter() { @Test void testEmailAddressDoesNotIncludeAccentsInTheLocalPart() { - String emailAddress = faker.internet().emailAddress("áéíóú"); + String emailAddress = internet.emailAddress("áéíóú"); assertThat(emailAddress).startsWith("aeiou@"); } @Test void testSafeEmailAddressDoesNotIncludeAccentsInTheLocalPart() { - String emailAddress = faker.internet().safeEmailAddress("áéíóú"); + String emailAddress = internet.safeEmailAddress("áéíóú"); assertThat(emailAddress).startsWith("aeiou@"); } @Test void testWebdomain() { - assertThat(faker.internet().webdomain()).matches("www\\.[\\w-]+\\.\\w+"); + assertThat(internet.webdomain()).matches("www\\.[\\w-]+\\.\\w+"); } - @RepeatedTest(10) - void testUrl() { + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "internet#url", repeat = 10) + void testUrl(String url) { // This test assumes that java.net.URL has better validation than we can come up with in // regex. - String url = faker.internet().url(); assertDoesNotThrow(() -> new URL(url)); } @Test void testImage() { - String imageUrl = faker.internet().image(); + String imageUrl = internet.image(); assertThat(imageUrl).matches("^https://picsum\\.photos/\\d{1,4}/\\d{1,4}$"); } @Test void testDomainName() { - assertThat(faker.internet().domainName()).matches("[a-z]+\\.\\w{2,4}"); + assertThat(internet.domainName()).matches("[a-z]+\\.\\w{2,4}"); } @Test void testDomainWord() { - assertThat(faker.internet().domainWord()).matches("[a-z]+"); + assertThat(internet.domainWord()).matches("[a-z]+"); } @Test void testDomainSuffix() { - assertThat(faker.internet().domainSuffix()).matches("\\w{2,4}"); + assertThat(internet.domainSuffix()).matches("\\w{2,4}"); } @Test void testImageWithExplicitParams() { - String imageUrl = faker.internet().image(800, 600, "lorem"); + String imageUrl = internet.image(800, 600, "lorem"); assertThat(imageUrl).matches("^https://picsum\\.photos/seed/lorem/800/600$"); } @Test void testHttpMethod() { - assertThat(faker.internet().httpMethod()).isNotEmpty(); + assertThat(internet.httpMethod()).isNotEmpty(); } @Test @SuppressWarnings("removal") void testPassword() { - assertThat(faker.internet().password()).matches("[a-z\\d]{8,16}"); + assertThat(internet.password()).matches("[a-z\\d]{8,16}"); } @Test @@ -253,62 +257,63 @@ void testPasswordWithFixedLength() { @Test @SuppressWarnings("removal") void testPasswordIncludeDigit() { - assertThat(faker.internet().password()).matches("[a-z\\d]{8,16}"); - assertThat(faker.internet().password(false)).matches("[a-z]{8,16}"); + assertThat(internet.password()).matches("[a-z\\d]{8,16}"); + assertThat(internet.password(false)).matches("[a-z]{8,16}"); } @Test @SuppressWarnings("removal") void testPasswordMinLengthMaxLength() { - assertThat(faker.internet().password(10, 25)).matches("[a-z\\d]{10,25}"); + assertThat(internet.password(10, 25)).matches("[a-z\\d]{10,25}"); } @Test @SuppressWarnings("removal") void testPasswordMinLengthMaxLengthIncludeUpperCase() { - assertThat(faker.internet().password(1, 2, false)).matches("[a-z\\d]{1,2}"); - assertThat(faker.internet().password(10, 25, true)).matches("[a-zA-Z\\d]{10,25}"); + assertThat(internet.password(1, 2, false)).matches("[a-z\\d]{1,2}"); + assertThat(internet.password(10, 25, true)).matches("[a-zA-Z\\d]{10,25}"); } @Test @SuppressWarnings("removal") void testPasswordMinLengthMaxLengthIncludeUpperCaseIncludeSpecial() { - assertThat(faker.internet().password(10, 25, false, false)).matches("[a-z\\d]{10,25}"); - assertThat(faker.internet().password(10, 25, false, true)).matches("[a-z\\d!@#$%^&*]{10,25}"); - assertThat(faker.internet().password(10, 25, true, true)).matches("[a-zA-Z\\d!@#$%^&*]{10,25}"); + assertThat(internet.password(10, 25, false, false)).matches("[a-z\\d]{10,25}"); + assertThat(internet.password(10, 25, false, true)).matches("[a-z\\d!@#$%^&*]{10,25}"); + assertThat(internet.password(10, 25, true, true)).matches("[a-zA-Z\\d!@#$%^&*]{10,25}"); } - @RepeatedTest(10) - void testPort() { - assertThat(faker.internet().port()).isBetween(0, 65535); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "internet#port", repeat = 10) + void testPort(int port) { + assertThat(port).isBetween(0, 65535); } @RepeatedTest(10) void portWithinGivenBounds() { - assertThat(faker.internet().port(0, 1)).isBetween(0, 1); - assertThat(faker.internet().port(100, 200)).isBetween(100, 200); - assertThat(faker.internet().port(1000, 1000)).isEqualTo(1000); - assertThat(faker.internet().port(65535, 65535)).isEqualTo(65535); + assertThat(internet.port(0, 1)).isBetween(0, 1); + assertThat(internet.port(100, 200)).isBetween(100, 200); + assertThat(internet.port(1000, 1000)).isEqualTo(1000); + assertThat(internet.port(65535, 65535)).isEqualTo(65535); } @RepeatedTest(10) void portWithinGivenRange() { - assertThat(faker.internet().port(WellKnownPorts)).isBetween(0, 1023); - assertThat(faker.internet().port(RegisteredPorts)).isBetween(1024, 49151); - assertThat(faker.internet().port(PortRange.DynamicPrivatePorts)).isBetween(49152, 65535); + assertThat(internet.port(WellKnownPorts)).isBetween(0, 1023); + assertThat(internet.port(RegisteredPorts)).isBetween(1024, 49151); + assertThat(internet.port(PortRange.DynamicPrivatePorts)).isBetween(49152, 65535); } @Test void portWithinGivenRange_validation() { - assertThatThrownBy(() -> faker.internet().port(-1, 100)) + assertThatThrownBy(() -> internet.port(-1, 100)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Port number -1 cannot be less than 0"); - assertThatThrownBy(() -> faker.internet().port(100, 99)) + assertThatThrownBy(() -> internet.port(100, 99)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Min (100) > Max (99)"); - assertThatThrownBy(() -> faker.internet().port(65535, 65536)) + assertThatThrownBy(() -> internet.port(65535, 65536)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Port number 65536 cannot be greater than 65535"); } @@ -318,7 +323,7 @@ void portWithinGivenRange_validation() { void shouldGenerateAPasswordWithMinAndMaxLength() { List results = new ArrayList<>(); for (int i = 0; i < 300; i++) { - results.add(faker.internet().password(1, 10)); + results.add(internet.password(1, 10)); } final List min = results.stream().filter(x -> x.length() == 1).toList(); @@ -331,10 +336,10 @@ void shouldGenerateAPasswordWithMinAndMaxLength() { @Test @SuppressWarnings("removal") void testPasswordMinLengthMaxLengthIncludeUpperCaseIncludeSpecialIncludeDigit() { - assertThat(faker.internet().password(10, 25, false, false, false)).matches("[a-z]{10,25}"); - assertThat(faker.internet().password(10, 25, false, true, true)).matches("[a-z\\d!@#$%^&*]{10,25}"); - assertThat(faker.internet().password(10, 25, true, true, false)).matches("[a-zA-Z!@#$%^&*]{10,25}"); - assertThat(faker.internet().password(10, 25, true, true, true)).matches("[a-zA-Z\\d!@#$%^&*]{10,25}"); + assertThat(internet.password(10, 25, false, false, false)).matches("[a-z]{10,25}"); + assertThat(internet.password(10, 25, false, true, true)).matches("[a-z\\d!@#$%^&*]{10,25}"); + assertThat(internet.password(10, 25, true, true, false)).matches("[a-zA-Z!@#$%^&*]{10,25}"); + assertThat(internet.password(10, 25, true, true, true)).matches("[a-zA-Z\\d!@#$%^&*]{10,25}"); } private Condition getCharacterCondition(char c, int expectedCnt) { @@ -358,29 +363,29 @@ private Condition getCharacterCondition(char c, int expectedCnt) { @Test void testMacAddress() { Condition colon = getCharacterCondition(':', 5); - assertThat(faker.internet().macAddress()).is(colon); - assertThat(faker.internet().macAddress("")).is(colon); + assertThat(internet.macAddress()).is(colon); + assertThat(internet.macAddress("")).is(colon); - assertThat(faker.internet().macAddress("fa:fa:fa")) + assertThat(internet.macAddress("fa:fa:fa")) .startsWith("fa:fa:fa") .is(colon); - assertThat(faker.internet().macAddress("01:02")) + assertThat(internet.macAddress("01:02")) .startsWith("01:02") .is(colon); // loop through 1000 times just to 'run it through the wringer' for (int i = 0; i < 1000; i++) { - assertThat(faker.internet().macAddress()).matches("[0-9a-fA-F]{2}(:([0-9a-fA-F]{1,4})){5}"); + assertThat(internet.macAddress()).matches("[0-9a-fA-F]{2}(:([0-9a-fA-F]{1,4})){5}"); } } @Test void testIpV4Address() { Condition colon = getCharacterCondition('.', 3); - assertThat(faker.internet().ipV4Address()).is(colon); + assertThat(internet.ipV4Address()).is(colon); for (int i = 0; i < 100; i++) { - final String[] octets = faker.internet().getIpV4Address().getHostAddress().split("\\."); + final String[] octets = internet.getIpV4Address().getHostAddress().split("\\."); assertThat(parseInt(octets[0])).isBetween(0, 255); assertThat(parseInt(octets[1])).isBetween(0, 255); assertThat(parseInt(octets[2])).isBetween(0, 255); @@ -390,12 +395,12 @@ void testIpV4Address() { @Test void testIpV4Cidr() { - assertThat(faker.internet().ipV4Cidr()) + assertThat(internet.ipV4Cidr()) .is(getCharacterCondition('.', 3)) .is(getCharacterCondition('/', 1)); for (int i = 0; i < 1000; i++) { - assertThat(parseInt(faker.internet().ipV4Cidr().split("/")[1])) + assertThat(parseInt(internet.ipV4Cidr().split("/")[1])) .isBetween(1, 32); } } @@ -409,7 +414,7 @@ void testPrivateIpV4Address() { String oneSevenTwo = "^172\\.(16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)\\..+"; for (int i = 0; i < 1000; i++) { - String addr = faker.internet().getPrivateIpV4Address().getHostAddress(); + String addr = internet.getPrivateIpV4Address().getHostAddress(); assertThat(addr).is(anyOf( new Condition<>(s -> s.matches(tenDot), "tenDot"), new Condition<>(s -> s.matches(oneTwoSeven), "oneTwoSeven"), @@ -429,7 +434,7 @@ void testPublicIpV4Address() { String oneSevenTwo = "^172\\.(16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)\\."; for (int i = 0; i < 1000; i++) { - String addr = faker.internet().getPublicIpV4Address().getHostAddress(); + String addr = internet.getPublicIpV4Address().getHostAddress(); assertThat(addr).doesNotMatch(tenDot) .doesNotMatch(oneTwoSeven) .doesNotMatch(oneSixNine) @@ -440,33 +445,34 @@ void testPublicIpV4Address() { @Test void testIpV6() { - assertThat(faker.internet().ipV6Address()).is(getCharacterCondition(':', 7)); + assertThat(internet.ipV6Address()).is(getCharacterCondition(':', 7)); for (int i = 0; i < 1000; i++) { - assertThat(faker.internet().getIpV6Address().getHostAddress()).matches(IPV6_HOST_ADDRESS); + assertThat(internet.getIpV6Address().getHostAddress()).matches(IPV6_HOST_ADDRESS); } } @Test void testIpV6Cidr() { - assertThat(faker.internet().ipV6Cidr()) + assertThat(internet.ipV6Cidr()) .is(getCharacterCondition(':', 7)) .is(getCharacterCondition('/', 1)); for (int i = 0; i < 1000; i++) { - assertThat(parseInt(faker.internet().ipV6Cidr().split("/")[1])) + assertThat(parseInt(internet.ipV6Cidr().split("/")[1])) .isBetween(1, 128); } } @RepeatedTest(10) void testSlugWithParams() { - assertThat(faker.internet().slug(List.of("a", "b"), "-")).matches("[a-zA-Z]+-[a-zA-Z]+"); + assertThat(internet.slug(List.of("a", "b"), "-")).matches("[a-zA-Z]+-[a-zA-Z]+"); } - @RepeatedTest(10) - void testSlug() { - assertThat(faker.internet().slug()).matches("[a-zA-Z]+_[a-zA-Z]+"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "internet#slug", repeat = 10) + void testSlug(String slug) { + assertThat(slug).matches("[a-zA-Z]+_[a-zA-Z]+"); } @Test @@ -488,24 +494,28 @@ void testUuidv3ConstantRandomSeed() { assertThat(faker1Uuidv3Third).isEqualTo(faker2Uuidv3Third); } - @RepeatedTest(10) - void testUuidv3() { - assertThat(faker.internet().uuidv3()).matches("^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "internet#uuidv3", repeat = 10) + void testUuidv3(String uuidv3) { + assertThat(uuidv3).matches("^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"); } - @RepeatedTest(10) - void testUuid() { - assertThat(faker.internet().uuid()).matches("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "internet#uuid", repeat = 10) + void testUuid(String uuid) { + assertThat(uuid).matches("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"); } - @RepeatedTest(10) - void testUuidv4() { - assertThat(faker.internet().uuidv4()).matches("^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "internet#uuidv4", repeat = 10) + void testUuidv4(String uuidv4) { + assertThat(uuidv4).matches("^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"); } - @RepeatedTest(10) - void testUuidv7() { - assertThat(faker.internet().uuidv7()).matches("^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"); + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "internet#uuidv7", repeat = 10) + void testUuidv7(String uuidv7) { + assertThat(uuidv7).matches("^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"); } @RepeatedTest(10) @@ -522,27 +532,27 @@ void testFarsiIDNs() { @Test void testUserAgent() { Internet.UserAgent[] agents = Internet.UserAgent.values(); - for (var agent : agents) { - assertThat(faker.internet().userAgent(agent)).isNotEmpty(); + for (Internet.UserAgent agent : agents) { + assertThat(internet.userAgent(agent)).isNotEmpty(); } //Test faker.internet().userAgentAny() for random user_agent retrieval. - assertThat(faker.internet().userAgent()).isNotEmpty(); + assertThat(internet.userAgent()).isNotEmpty(); } @Test void testBotUserAgent() { Internet.BotUserAgent[] agents = Internet.BotUserAgent.values(); - for (var agent : agents) { - assertThat(faker.internet().botUserAgent(agent)).isNotEmpty(); + for (Internet.BotUserAgent agent : agents) { + assertThat(internet.botUserAgent(agent)).isNotEmpty(); } //Test faker.internet().userAgentAny() for random user_agent retrieval. - assertThat(faker.internet().botUserAgentAny()).isNotEmpty(); + assertThat(internet.botUserAgentAny()).isNotEmpty(); } @Test void testSlugWithNull() { - assertThat(faker.internet().slug(null, "_")).isNotNull(); + assertThat(internet.slug(null, "_")).isNotNull(); } } diff --git a/src/test/java/net/datafaker/providers/base/StockTest.java b/src/test/java/net/datafaker/providers/base/StockTest.java index 000fbe8eb..ad4d84ae5 100644 --- a/src/test/java/net/datafaker/providers/base/StockTest.java +++ b/src/test/java/net/datafaker/providers/base/StockTest.java @@ -2,8 +2,9 @@ import static org.assertj.core.api.Assertions.assertThat; +import net.datafaker.junit.FakerSource; import org.apache.commons.validator.routines.ISINValidator; -import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.params.ParameterizedTest; import java.util.Collection; import java.util.List; @@ -22,13 +23,14 @@ protected Collection providerListTest() { TestSpec.of(stock::exchanges, "stock.exchanges")); } - @RepeatedTest(100) - void isin() { - assertThat(faker.stock().isin()) + @ParameterizedTest(name = "[{index}] {0}") + @FakerSource(code = "stock#isin", repeat = 100) + void isin(String isin) { + assertThat(isin) .matches("^[A-Z]{2}[0-9A-Z]{9}[0-9]$") - .satisfies(isin -> assertThat(ISIN_VALIDATOR.isValid(isin)) - .as("ISIN %s should be valid according to %s", isin, ISIN_VALIDATOR.getClass().getName()) - .isTrue()); + .satisfies(i -> assertThat(ISIN_VALIDATOR.isValid(i)) + .as("ISIN %s should be valid according to %s", i, ISIN_VALIDATOR.getClass().getName()) + .isTrue()); } }