diff --git a/build.gradle b/build.gradle index 3e77eaa..ba5004f 100644 --- a/build.gradle +++ b/build.gradle @@ -10,8 +10,20 @@ repositories { } dependencies { + testImplementation 'org.mockito:mockito-core:5.14.2' + testImplementation 'org.mockito:mockito-junit-jupiter:5.14.2' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.4' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.4' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.4' } test { + systemProperty "file.encoding", "utf-8" useJUnitPlatform() -} \ No newline at end of file +} + +compileJava.options.encoding = 'UTF-8' + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 41dfb87..b6dbf84 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Tue Feb 11 23:22:06 MSK 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/com/walking/lesson125_unit_testing/Main.java b/src/main/java/com/walking/lesson125_unit_testing/Main.java index e6fae03..98e8870 100644 --- a/src/main/java/com/walking/lesson125_unit_testing/Main.java +++ b/src/main/java/com/walking/lesson125_unit_testing/Main.java @@ -1,9 +1,5 @@ package com.walking.lesson125_unit_testing; - -import com.walking.lesson125_unit_testing.exception.RegexValidationException; -import com.walking.lesson125_unit_testing.model.FullName; - /** * На базе вашего решения * задачи из урока 125 @@ -11,67 +7,6 @@ * вашем предыдущем решении. */ public class Main { - - public static final String FULL_NAME_REGEX = "^[А-Я][А-Яа-я-]* [А-Я][а-я]* [А-Я][а-я]+$"; - public static final String DOUBLE_SURNAME_REGEX = "[А-Я][а-я]*-[А-Я][а-я]*"; - public static final String NAME_REGEX = "[А-Я][а-я]*"; - public static final String PATRONYMIC_REGEX = "[А-Я][а-я]+"; - public static void main(String[] args) { - System.out.println(parseName("Иванов Иван Иванович")); - System.out.println(parseName("Иванов-Иванов Иван Иванович")); - System.out.println(parseName("Иванов-Иванов И Иванович")); - System.out.println(parseName("И-Иванов И Иванович")); - System.out.println(parseName("Иванов иван Иванович")); -// Все равно упадет на 30й строке. -// System.out.println(parseName("И-иванов И Иванович")); -// System.out.println(parseName("Иванов Иван иванович")); -// System.out.println(parseName("ИваНов Иван Иванович")); -// System.out.println(parseName("Ivanov Ivan")); - } - - private static FullName parseName(String nameString) { - if (!nameString.matches(FULL_NAME_REGEX)) { - throw new RegexValidationException(nameString, FULL_NAME_REGEX); - } - - FullName fullName = new FullName(); - String[] splitNameString = nameString.split(" "); - - String surname = splitNameString[0]; - validateSurname(surname); - fullName.setSurname(surname); - - String name = splitNameString[1]; - validateName(name); - fullName.setName(name); - - String patronymic = splitNameString[2]; - validatePatronymic(patronymic); - fullName.setPatronymic(patronymic); - - return fullName; - } - - private static void validateSurname(String surname) { - if (surname.contains("-")) { - if (!surname.matches(DOUBLE_SURNAME_REGEX)) { - throw new RegexValidationException(surname, DOUBLE_SURNAME_REGEX); - } - } else { - validateName(surname); - } - } - - private static void validateName(String name) { - if (!name.matches(NAME_REGEX)) { - throw new RegexValidationException(name, NAME_REGEX); - } - } - - private static void validatePatronymic(String name) { - if (!name.matches(PATRONYMIC_REGEX)) { - throw new RegexValidationException(name, PATRONYMIC_REGEX); - } } } diff --git a/src/main/java/com/walking/lesson125_unit_testing/model/FullName.java b/src/main/java/com/walking/lesson125_unit_testing/model/FullName.java index 86c5f1c..65bc32b 100644 --- a/src/main/java/com/walking/lesson125_unit_testing/model/FullName.java +++ b/src/main/java/com/walking/lesson125_unit_testing/model/FullName.java @@ -1,5 +1,7 @@ package com.walking.lesson125_unit_testing.model; +import java.util.Objects; + public class FullName { private String name; private String surname; @@ -42,4 +44,30 @@ public void setPatronymic(String patronymic) { public String toString() { return "%s %s %s".formatted(surname, name, patronymic); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + FullName fullName = (FullName) o; + + return Objects.equals(name, fullName.name) && Objects.equals(surname, fullName.surname) + && Objects.equals(patronymic, fullName.patronymic); + } + + @Override + public int hashCode() { + int result = Objects.hashCode(name); + + result = 31 * result + Objects.hashCode(surname); + result = 31 * result + Objects.hashCode(patronymic); + + return result; + } } diff --git a/src/main/java/com/walking/lesson125_unit_testing/service/FullNameParsingService.java b/src/main/java/com/walking/lesson125_unit_testing/service/FullNameParsingService.java new file mode 100644 index 0000000..b94c83e --- /dev/null +++ b/src/main/java/com/walking/lesson125_unit_testing/service/FullNameParsingService.java @@ -0,0 +1,32 @@ +package com.walking.lesson125_unit_testing.service; + +import com.walking.lesson125_unit_testing.model.FullName; + +public class FullNameParsingService { + private final FullNameValidationService fullNameValidationService; + + public FullNameParsingService(FullNameValidationService fullNameValidationService) { + this.fullNameValidationService = fullNameValidationService; + } + + public FullName parseFullName(String nameString) { + if (nameString == null) { + throw new IllegalArgumentException("Unable parse null"); + } + + fullNameValidationService.validateFullName(nameString); + + String[] splitNameString = nameString.split(" "); + + String surname = splitNameString[0]; + fullNameValidationService.validateSurname(surname); + + String name = splitNameString[1]; + fullNameValidationService.validateName(name); + + String patronymic = splitNameString[2]; + fullNameValidationService.validatePatronymic(patronymic); + + return new FullName(name, surname, patronymic); + } +} diff --git a/src/main/java/com/walking/lesson125_unit_testing/service/FullNameValidationService.java b/src/main/java/com/walking/lesson125_unit_testing/service/FullNameValidationService.java new file mode 100644 index 0000000..0d003a3 --- /dev/null +++ b/src/main/java/com/walking/lesson125_unit_testing/service/FullNameValidationService.java @@ -0,0 +1,38 @@ +package com.walking.lesson125_unit_testing.service; + +import com.walking.lesson125_unit_testing.exception.RegexValidationException; + +public class FullNameValidationService { + private static final String FULL_NAME_REGEX = "^[А-Я][А-Яа-я-]* [А-Я][а-я]* [А-Я][а-я]+$"; + private static final String DOUBLE_SURNAME_REGEX = "[А-Я][а-я]*-[А-Я][а-я]*"; + private static final String NAME_REGEX = "[А-Я][а-я]*"; + private static final String PATRONYMIC_REGEX = "[А-Я][а-я]+"; + + public void validateFullName(String nameString) { + if (!nameString.matches(FULL_NAME_REGEX)) { + throw new RegexValidationException(nameString, FULL_NAME_REGEX); + } + } + + public void validateSurname(String surname) { + if (surname.contains("-")) { + if (!surname.matches(DOUBLE_SURNAME_REGEX)) { + throw new RegexValidationException(surname, DOUBLE_SURNAME_REGEX); + } + } else { + validateName(surname); + } + } + + public void validateName(String name) { + if (!name.matches(NAME_REGEX)) { + throw new RegexValidationException(name, NAME_REGEX); + } + } + + public void validatePatronymic(String name) { + if (!name.matches(PATRONYMIC_REGEX)) { + throw new RegexValidationException(name, PATRONYMIC_REGEX); + } + } +} diff --git a/src/test/java/com/walking/lesson125_unit_testing/service/FullNameParsingServiceTest.java b/src/test/java/com/walking/lesson125_unit_testing/service/FullNameParsingServiceTest.java new file mode 100644 index 0000000..03958bb --- /dev/null +++ b/src/test/java/com/walking/lesson125_unit_testing/service/FullNameParsingServiceTest.java @@ -0,0 +1,72 @@ +package com.walking.lesson125_unit_testing.service; + +import com.walking.lesson125_unit_testing.exception.RegexValidationException; +import com.walking.lesson125_unit_testing.model.FullName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.function.Executable; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class FullNameParsingServiceTest { + @InjectMocks + private FullNameParsingService fullNameParsingService; + + @Mock + private FullNameValidationService fullNameValidationService; + + @Test + void parseFullName_success() { +// given + FullName expected = new FullName("Иван", "Иванов", "Иванович"); + +// when + FullName result = fullNameParsingService.parseFullName("Иванов Иван Иванович"); + +// then + assertEquals(expected, result); + + verify(fullNameValidationService).validateFullName(anyString()); + verify(fullNameValidationService).validateSurname(anyString()); + verify(fullNameValidationService).validateName(anyString()); + verify(fullNameValidationService).validatePatronymic(anyString()); + } + + @Test + void parseFullName_failed_with_invalidFullName() { +// given + doThrow(RegexValidationException.class).when(fullNameValidationService) + .validateFullName(anyString()); + +// when + Executable actual = () -> fullNameParsingService.parseFullName(anyString()); + +// then + assertThrows(RegexValidationException.class, actual); + + verify(fullNameValidationService).validateFullName(anyString()); + + verify(fullNameValidationService, never()).validateSurname(anyString()); + verify(fullNameValidationService, never()).validateName(anyString()); + verify(fullNameValidationService, never()).validatePatronymic(anyString()); + } + + @Test + void parseFullName_failed_with_null() { +// when + Executable actual = () -> fullNameParsingService.parseFullName(null); + +// then + assertThrows(IllegalArgumentException.class, actual); + + verify(fullNameValidationService, never()).validateFullName(anyString()); + verify(fullNameValidationService, never()).validateSurname(anyString()); + verify(fullNameValidationService, never()).validateName(anyString()); + verify(fullNameValidationService, never()).validatePatronymic(anyString()); + } +} diff --git a/src/test/java/com/walking/lesson125_unit_testing/service/FullNameValidationServiceTest.java b/src/test/java/com/walking/lesson125_unit_testing/service/FullNameValidationServiceTest.java new file mode 100644 index 0000000..0cd1eb6 --- /dev/null +++ b/src/test/java/com/walking/lesson125_unit_testing/service/FullNameValidationServiceTest.java @@ -0,0 +1,166 @@ +package com.walking.lesson125_unit_testing.service; + +import com.walking.lesson125_unit_testing.exception.RegexValidationException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class FullNameValidationServiceTest { + private final FullNameValidationService fullNameValidationService = + new FullNameValidationService(); + + @ParameterizedTest + @MethodSource("sourceValidFullName") + void validateFullName_success(String validFullName) { +// when + Executable actual = () -> fullNameValidationService.validateFullName(validFullName); + +// then + assertDoesNotThrow(actual); + } + + @ParameterizedTest + @MethodSource("sourceInvalidFullNames") + void validateFullName_failed_with_invalidFullName(String invalidFullName) { +// when + Executable actual = () -> fullNameValidationService.validateFullName(invalidFullName); + +// then + assertThrows(RegexValidationException.class, actual); + } + + @Test + void validateFullName_failed_with_null() { +// when + Executable actual = () -> fullNameValidationService.validateFullName(null); + +// then + assertThrows(NullPointerException.class, actual); + } + + @ParameterizedTest + @MethodSource("sourceValidNames") + void validateName_success(String validName) { +// when + Executable actual = () -> fullNameValidationService.validateName(validName); + +// then + assertDoesNotThrow(actual); + } + + @ParameterizedTest + @MethodSource("sourceInvalidNames") + void validateName_failed_with_invalidName(String invalidName) { +// when + Executable actual = () -> fullNameValidationService.validateName(invalidName); + +// then + assertThrows(RegexValidationException.class, actual); + } + + @Test + void validateName_failed_with_null() { +// when + Executable actual = () -> fullNameValidationService.validateName(null); + +// then + assertThrows(NullPointerException.class, actual); + } + + @ParameterizedTest + @MethodSource("sourceValidDoubleSurnames") + void validateSurName_success(String validSurname) { +// when + Executable actual = () -> fullNameValidationService.validateSurname(validSurname); + +// then + assertDoesNotThrow(actual); + } + + @ParameterizedTest + @MethodSource("sourceInvalidDoubleSurnames") + void validateSurname_failed_with_invalidSurname(String invalidSurname) { +// when + Executable actual = () -> fullNameValidationService.validateSurname(invalidSurname); + +// then + assertThrows(RegexValidationException.class, actual); + } + + @Test + void validateSurname_failed_with_null() { +// when + Executable actual = () -> fullNameValidationService.validateSurname(null); + +// then + assertThrows(NullPointerException.class, actual); + } + + @ParameterizedTest + @MethodSource("sourceValidPatronymics") + void validatePatronymic_success(String validPatronymic) { +// when + Executable actual = () -> fullNameValidationService.validatePatronymic(validPatronymic); + +// then + assertDoesNotThrow(actual); + } + + @ParameterizedTest + @MethodSource("sourceInvalidPatronymics") + void validatePatronymic_failed_with_invalidPatronymic(String invalidPatronymic) { +// when + Executable actual = () -> fullNameValidationService.validatePatronymic(invalidPatronymic); + +// then + assertThrows(RegexValidationException.class, actual); + } + + @Test + void validatePatronymic_failed_with_null() { +// when + Executable actual = () -> fullNameValidationService.validatePatronymic(null); + +// then + assertThrows(NullPointerException.class, actual); + } + + static List sourceValidFullName() { + return List.of("Иванов Иван Иванович", "Иванов-Иванов Иван Иванович", + "Иванов-Иванов И Иванович", "И-Иванов И Иванович"); + } + + static List sourceInvalidFullNames() { + return List.of("", "иванов Иван Иванович", "Иванов иван Иванович", "Иванов Иван иванович", + "Иванов Иван", "Ivanov Ivan"); + } + + static List sourceValidNames() { + return List.of("Иван"); + } + + static List sourceInvalidNames() { + return List.of("", "иван", "Ivan", "Иван-иван"); + } + + static List sourceValidDoubleSurnames() { + return List.of("Иванов-Иванов", "И-Иванов"); + } + + static List sourceInvalidDoubleSurnames() { + return List.of("", "И-иванов", "иванов-И", "Иванов-И-Иванов", "Ivanov-I"); + } + + static List sourceValidPatronymics() { + return List.of("Иванович"); + } + + static List sourceInvalidPatronymics() { + return List.of("", "иванович", "Ivanovich", "И", "Иванович-Степанович"); + } +}