Add parameterized module: JUnit 5 @ParameterizedTest argument sources, display names, and two real anti-pattern failures
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class AuthService {
|
||||
|
||||
private static final Set<Role> WRITE_ROLES = Set.of(Role.ADMIN, Role.SUPERUSER);
|
||||
|
||||
public boolean canWrite(Role role) {
|
||||
return WRITE_ROLES.contains(role);
|
||||
}
|
||||
|
||||
public boolean canRead(Role role) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean canDelete(Role role) {
|
||||
return role == Role.ADMIN;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
public class Calculator {
|
||||
|
||||
public int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public int multiply(int a, int b) {
|
||||
return a * b;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class Cms {
|
||||
|
||||
private static final Set<String> PUBLISHING_ROLES = Set.of("EDITOR", "ADMIN");
|
||||
|
||||
public boolean canPublish(int userId, String role) {
|
||||
return PUBLISHING_ROLES.contains(role);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
/** A deliberately mutable object, used to demonstrate the shared-mutable-argument anti-pattern. */
|
||||
public class Config {
|
||||
|
||||
private int callCount = 0;
|
||||
|
||||
public void touch() {
|
||||
callCount++;
|
||||
}
|
||||
|
||||
public int getCallCount() {
|
||||
return callCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Config[callCount=" + callCount + "]";
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class CurrencyService {
|
||||
|
||||
private static final Set<String> KNOWN_ISO_CODES = Set.of(
|
||||
"USD", "EUR", "GBP", "JPY", "INR", "AUD", "CHF"
|
||||
);
|
||||
|
||||
public boolean isValidCode(String code) {
|
||||
return KNOWN_ISO_CODES.contains(code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class Geocoder {
|
||||
|
||||
private static final Map<String, String> REGION_BY_POSTCODE = Map.of(
|
||||
"SW1A", "London",
|
||||
"M1", "Manchester",
|
||||
"EH1", "Edinburgh",
|
||||
"CF10", "Cardiff",
|
||||
"BT1", "Belfast"
|
||||
);
|
||||
|
||||
public String getRegion(String postcode) {
|
||||
String region = REGION_BY_POSTCODE.get(postcode);
|
||||
if (region == null) {
|
||||
throw new IllegalArgumentException("unknown postcode: " + postcode);
|
||||
}
|
||||
return region;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
public enum Role {
|
||||
ADMIN, SUPERUSER, EDITOR, USER, GUEST
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
public class ScenarioProcessor {
|
||||
|
||||
private static final int MAX_ALLOWED = 999;
|
||||
|
||||
public boolean evaluate(String scenarioId, int value) {
|
||||
if (value < 0 || value > MAX_ALLOWED) {
|
||||
return false;
|
||||
}
|
||||
return switch (scenarioId) {
|
||||
case "zero_value" -> false;
|
||||
default -> true;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* A subscription account. {@code renewalDueDate} is when the subscription is next due for
|
||||
* renewal; an overdue renewal still counts as active for a short grace window, as long as the
|
||||
* account itself hasn't been disabled.
|
||||
*/
|
||||
public class User {
|
||||
|
||||
private static final int RENEWAL_GRACE_PERIOD_DAYS = 3;
|
||||
|
||||
private final String name;
|
||||
private final LocalDate renewalDueDate;
|
||||
private final boolean enabled;
|
||||
|
||||
public User(String name, LocalDate renewalDueDate, boolean enabled) {
|
||||
this.name = name;
|
||||
this.renewalDueDate = renewalDueDate;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return enabled && renewalDueDate.isAfter(LocalDate.now().minusDays(RENEWAL_GRACE_PERIOD_DAYS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class UserService {
|
||||
|
||||
private final Map<String, String> passwordsByUsername = new HashMap<>();
|
||||
|
||||
public void register(String username, String password) {
|
||||
if (username == null || username.isBlank()) {
|
||||
throw new IllegalArgumentException("username must not be null or blank");
|
||||
}
|
||||
passwordsByUsername.put(username, password);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class ArgumentsSourceTest {
|
||||
|
||||
private final ScenarioProcessor processor = new ScenarioProcessor();
|
||||
|
||||
@ParameterizedTest(name = "scenario: {0}")
|
||||
@ArgumentsSource(ScenarioArgumentsProvider.class)
|
||||
void testWithExternalScenarios(String scenarioId, int value, boolean expected) {
|
||||
assertEquals(expected, processor.evaluate(scenarioId, value));
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.converter.ConvertWith;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class ConvertWithTest {
|
||||
|
||||
@ParameterizedTest(name = "date {0} should be in 2023")
|
||||
@ValueSource(strings = {"2023-01-01", "2023-06-15", "2023-12-31"})
|
||||
void testDatesAreIn2023(@ConvertWith(DashDateConverter.class) LocalDate date) {
|
||||
assertEquals(2023, date.getYear());
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvFileSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class CsvFileSourceTest {
|
||||
|
||||
private final Geocoder geocoder = new Geocoder();
|
||||
|
||||
@ParameterizedTest(name = "postcode {0} should map to region {1}")
|
||||
@CsvFileSource(resources = "/test-data/postcode-regions.csv", numLinesToSkip = 1)
|
||||
void shouldMapPostcodeToRegion(String postcode, String expectedRegion) {
|
||||
assertEquals(expectedRegion, geocoder.getRegion(postcode));
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class CsvSourceTest {
|
||||
|
||||
private final Calculator calculator = new Calculator();
|
||||
private final Cms cms = new Cms();
|
||||
|
||||
@ParameterizedTest(name = "{0} + {1} = {2}")
|
||||
@CsvSource({
|
||||
"10, 5, 15",
|
||||
"0, 0, 0",
|
||||
"-3, 7, 4",
|
||||
"100, -50, 50"
|
||||
})
|
||||
void shouldAddNumbers(int a, int b, int expected) {
|
||||
assertEquals(expected, calculator.add(a, b));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "user {0} with role {1}")
|
||||
@CsvSource(useHeadersInDisplayName = true, textBlock = """
|
||||
USER_ID, ROLE, CAN_PUBLISH
|
||||
# Elevated users
|
||||
1001, EDITOR, true
|
||||
1002, ADMIN, true
|
||||
# Standard users
|
||||
1003, USER, false
|
||||
""")
|
||||
void testPublishingPermissions(int userId, String role, boolean canPublish) {
|
||||
assertEquals(canPublish, cms.canPublish(userId, role));
|
||||
}
|
||||
|
||||
// Same flag and headers as testPublishingPermissions above, reduced to one row and with no
|
||||
// custom `name` -- shows what useHeadersInDisplayName actually changes on its own: the
|
||||
// auto-generated default display name. Compare this invocation's name against
|
||||
// testPublishingPermissions' above (same flag, custom name) and against
|
||||
// HeaderNamePlaceholderMistakeTest (same flag, an attempt to reference headers from a
|
||||
// custom name, which fails).
|
||||
@ParameterizedTest
|
||||
@CsvSource(useHeadersInDisplayName = true, textBlock = """
|
||||
USER_ID, ROLE, CAN_PUBLISH
|
||||
1001, EDITOR, true
|
||||
""")
|
||||
void testPublishingPermissionsDefaultDisplayName(int userId, String role, boolean canPublish) {
|
||||
assertEquals(canPublish, cms.canPublish(userId, role));
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.converter.SimpleArgumentConverter;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
class DashDateConverter extends SimpleArgumentConverter {
|
||||
@Override
|
||||
protected Object convert(Object source, Class<?> targetType) {
|
||||
return LocalDate.parse((String) source, DateTimeFormatter.ISO_LOCAL_DATE);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class DisplayNameTest {
|
||||
|
||||
private final Calculator calculator = new Calculator();
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0} × {1} = {2}")
|
||||
@CsvSource({"3, 4, 12", "0, 5, 0", "-2, 3, -6"})
|
||||
void shouldMultiply(int a, int b, int expected) {
|
||||
assertEquals(expected, calculator.multiply(a, b));
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class EnumSourceTest {
|
||||
|
||||
private final AuthService authService = new AuthService();
|
||||
|
||||
@ParameterizedTest(name = "{0} should have write access")
|
||||
@EnumSource(value = Role.class, names = {"ADMIN", "SUPERUSER"})
|
||||
void privilegedRolesShouldHaveWriteAccess(Role role) {
|
||||
assertTrue(authService.canWrite(role));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0} must NOT have write access")
|
||||
@EnumSource(value = Role.class,
|
||||
names = {"ADMIN", "SUPERUSER"},
|
||||
mode = EnumSource.Mode.EXCLUDE)
|
||||
void nonPrivilegedRolesMustNotWrite(Role role) {
|
||||
assertFalse(authService.canWrite(role));
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.FieldSource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class FieldSourceTest {
|
||||
|
||||
static final List<String> VALID_ISO_CODES = List.of(
|
||||
"USD", "EUR", "GBP", "JPY", "INR", "AUD", "CHF"
|
||||
);
|
||||
|
||||
private final CurrencyService currencyService = new CurrencyService();
|
||||
|
||||
@ParameterizedTest(name = "ISO code {0} should be recognized")
|
||||
@FieldSource("VALID_ISO_CODES")
|
||||
void shouldRecognizeIsoCurrencyCode(String code) {
|
||||
assertTrue(currencyService.isValidCode(code));
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
/**
|
||||
* This class is not meant to compile into a passing test -- it exists only to let
|
||||
* docs/output/06-header-name-placeholder-throws.txt capture the real exception that a
|
||||
* {@code name} template referencing a CSV header by name (instead of a positional index)
|
||||
* produces. The method is {@code @Disabled} in the committed version; the failure was captured
|
||||
* by temporarily removing that annotation and running this class alone. See the companion post
|
||||
* for why {@code {USER_ID}} is not valid syntax here even with {@code useHeadersInDisplayName}.
|
||||
*/
|
||||
class HeaderNamePlaceholderMistakeTest {
|
||||
|
||||
@org.junit.jupiter.api.Disabled("Kept disabled so the module's build stays green -- "
|
||||
+ "see docs/output/06-header-name-placeholder-throws.txt for the real failure "
|
||||
+ "this produces when the @Disabled annotation is removed.")
|
||||
@ParameterizedTest(name = "user {USER_ID} with role {ROLE}")
|
||||
@CsvSource(useHeadersInDisplayName = true, textBlock = """
|
||||
USER_ID, ROLE, CAN_PUBLISH
|
||||
1001, EDITOR, true
|
||||
""")
|
||||
void nameTemplateCannotReferenceHeadersByName(int userId, String role, boolean canPublish) {
|
||||
// Never reached -- JUnit rejects the display-name pattern before this method runs.
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class MethodSourceTest {
|
||||
|
||||
@ParameterizedTest(name = "user {0} active={1}")
|
||||
@MethodSource("activeUserProvider")
|
||||
void testUserActiveStatus(User user, boolean expectedActive) {
|
||||
assertEquals(expectedActive, user.isActive());
|
||||
}
|
||||
|
||||
static Stream<Arguments> activeUserProvider() {
|
||||
return Stream.of(
|
||||
Arguments.of(new User("alice", LocalDate.now().minusDays(1), true), true),
|
||||
Arguments.of(new User("bob", LocalDate.now().plusDays(30), true), true),
|
||||
Arguments.of(new User("carol", LocalDate.now().minusDays(1), false), false)
|
||||
);
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class MixedAssertionsAntiPatternTest {
|
||||
|
||||
private final AuthService auth = new AuthService();
|
||||
|
||||
// BAD: different assertions encoded by role, in one parameterized test.
|
||||
@ParameterizedTest
|
||||
@CsvSource({"ADMIN, true, true", "GUEST, false, true"})
|
||||
void testPermissions(String role, boolean canDelete, boolean canRead) {
|
||||
assertEquals(canDelete, auth.canDelete(Role.valueOf(role)));
|
||||
assertEquals(canRead, auth.canRead(Role.valueOf(role))); // different assertion per row
|
||||
}
|
||||
|
||||
// BETTER: split into two focused parameterized tests, one behavior each.
|
||||
@ParameterizedTest
|
||||
@EnumSource(value = Role.class, names = "ADMIN")
|
||||
void adminShouldDelete(Role role) {
|
||||
assertTrue(auth.canDelete(role));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Role.class)
|
||||
void allRolesShouldRead(Role role) {
|
||||
assertTrue(auth.canRead(role));
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.NullAndEmptySource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class NullAndEmptySourceTest {
|
||||
|
||||
private final UserService userService = new UserService();
|
||||
|
||||
@ParameterizedTest(name = "blank username [{0}] should be rejected")
|
||||
@NullAndEmptySource
|
||||
@ValueSource(strings = {" ", "\t", "\n"})
|
||||
void shouldRejectBlankUsernames(String username) {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> userService.register(username, "password123"));
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
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.ParameterDeclarations;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class ScenarioArgumentsProvider implements ArgumentsProvider {
|
||||
|
||||
// ArgumentsProvider has carried two provideArguments overloads since 5.13: this one, which
|
||||
// also receives the test method's ParameterDeclarations, and a legacy single-argument form
|
||||
// that still compiles but is now deprecated. Prefer this one in new code.
|
||||
@Override
|
||||
public Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext ctx) {
|
||||
// Could load from a test database, JSON fixture, environment, etc.
|
||||
return Stream.of(
|
||||
Arguments.of("happy_path", 100, true),
|
||||
Arguments.of("zero_value", 0, false),
|
||||
Arguments.of("max_boundary", 999, true)
|
||||
);
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Two parameterized tests, same shape, one real difference: whether the data source hands out
|
||||
* one shared {@link Config} instance or a fresh one per invocation. Both tests assert the same
|
||||
* thing -- "this invocation's Config has been touched exactly once" -- which is true in
|
||||
* isolation but only reliable in the GOOD version.
|
||||
*/
|
||||
class SharedMutableArgumentAntiPatternTest {
|
||||
|
||||
@org.junit.jupiter.api.Disabled("Kept disabled so the module's build stays green -- see "
|
||||
+ "docs/output/09-shared-mutable-argument-failure.txt for the real failure this "
|
||||
+ "produces on its second invocation when the @Disabled annotation is removed.")
|
||||
@ParameterizedTest
|
||||
@MethodSource("sharedProvider")
|
||||
void badSharedConfigAccumulatesTouches(Config config) {
|
||||
config.touch();
|
||||
assertEquals(1, config.getCallCount());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("freshProvider")
|
||||
void goodFreshConfigIsAlwaysTouchedOnce(Config config) {
|
||||
config.touch();
|
||||
assertEquals(1, config.getCallCount());
|
||||
}
|
||||
|
||||
// BAD: one Config instance is built once and handed to every invocation.
|
||||
static Stream<Arguments> sharedProvider() {
|
||||
Config shared = new Config();
|
||||
return Stream.of(
|
||||
Arguments.of(shared), // invocation 1 touches it -> callCount becomes 1
|
||||
Arguments.of(shared) // invocation 2 touches the SAME instance -> callCount becomes 2
|
||||
);
|
||||
}
|
||||
|
||||
// GOOD: a fresh Config is built inside the Stream for every invocation.
|
||||
static Stream<Arguments> freshProvider() {
|
||||
return Stream.of(
|
||||
Arguments.of(new Config()),
|
||||
Arguments.of(new Config())
|
||||
);
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.ankurm.tutorials.junit.parameterized;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ValueSourceTest {
|
||||
|
||||
@ParameterizedTest(name = "{0} should be a palindrome")
|
||||
@ValueSource(strings = {"racecar", "radar", "level"})
|
||||
void shouldIdentifyPalindromes(String candidate) {
|
||||
assertTrue(candidate.equals(new StringBuilder(candidate).reverse().toString()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
postcode,region
|
||||
SW1A,London
|
||||
M1,Manchester
|
||||
EH1,Edinburgh
|
||||
CF10,Cardiff
|
||||
BT1,Belfast
|
||||
|
Reference in New Issue
Block a user