3.1 Лабораторийн зорилго
Энэ лабораторид та JUnit 5 ашиглан unit тест бичих чадвар эзэмшинэ:
- JUnit 5 суулгах ба анхны тест бичих
- Хил заагийн шинжилгээ (Boundary Value) ашиглан тестлэх
- Exception тестлэх
- TDD аргаар шинэ функц хөгжүүлэх
Шаардлагатай зүйлс: Java JDK 17+, Eclipse IDE, Maven
3.2 Лаб 1: JUnit 5 суулгах ба анхны тест
Алхам 1: Eclipse-д шинэ Maven төсөл үүсгэнэ (File → New → Maven Project): TestingLab
Алхам 2: pom.xml файлд JUnit 5 хамаарлыг нэмнэ:
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Алхам 3: src/main/java дотор StringUtils.java үүсгэнэ:
public class StringUtils {
// Тэмдэгт мөрийг урвуулах
public static String reverse(String str) {
if (str == null) {
throw new IllegalArgumentException("null утга оруулж болохгүй");
}
return new StringBuilder(str).reverse().toString();
}
// Palindrome мөн эсэхийг шалгах (урд хойноосоо адилхан)
public static boolean isPalindrome(String str) {
if (str == null) {
throw new IllegalArgumentException("null утга оруулж болохгүй");
}
String cleaned = str.toLowerCase().replaceAll("\\s+", "");
return cleaned.equals(new StringBuilder(cleaned).reverse().toString());
}
// Үгийн тоог тоолох
public static int countWords(String str) {
if (str == null || str.trim().isEmpty()) {
return 0;
}
return str.trim().split("\\s+").length;
}
// Эхний үсгийг том болгох
public static String capitalize(String str) {
if (str == null || str.isEmpty()) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
}
Алхам 4: src/test/java дотор StringUtilsTest.java үүсгэнэ:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import static org.junit.jupiter.api.Assertions.*;
class StringUtilsTest {
// === reverse() тестүүд ===
@Test
@DisplayName("Энгийн мөр урвуулах")
void reverse_normalString_returnsReversed() {
assertEquals("табА", StringUtils.reverse("Абат"));
}
@Test
@DisplayName("Хоосон мөр урвуулах")
void reverse_emptyString_returnsEmpty() {
assertEquals("", StringUtils.reverse(""));
}
@Test
@DisplayName("null оруулахад exception үүсэх")
void reverse_null_throwsException() {
assertThrows(IllegalArgumentException.class, () -> {
StringUtils.reverse(null);
});
}
// === isPalindrome() тестүүд ===
@Test
@DisplayName("'madam' нь palindrome")
void isPalindrome_madam_returnsTrue() {
assertTrue(StringUtils.isPalindrome("madam"));
}
@Test
@DisplayName("'hello' нь palindrome биш")
void isPalindrome_hello_returnsFalse() {
assertFalse(StringUtils.isPalindrome("hello"));
}
@Test
@DisplayName("Хоосон мөр palindrome мөн")
void isPalindrome_empty_returnsTrue() {
assertTrue(StringUtils.isPalindrome(""));
}
@Test
@DisplayName("Зайтай palindrome шалгах")
void isPalindrome_withSpaces_returnsTrue() {
assertTrue(StringUtils.isPalindrome("A man a plan a canal Panama"));
}
// === countWords() тестүүд ===
@Test
@DisplayName("3 үгтэй мөрийн үг тоолох")
void countWords_threeWords_returns3() {
assertEquals(3, StringUtils.countWords("Сайн байна уу"));
}
@Test
@DisplayName("Хоосон мөрийн үг тоолох")
void countWords_empty_returns0() {
assertEquals(0, StringUtils.countWords(""));
}
@Test
@DisplayName("null мөрийн үг тоолох")
void countWords_null_returns0() {
assertEquals(0, StringUtils.countWords(null));
}
// === capitalize() тестүүд ===
@Test
@DisplayName("Эхний үсгийг том болгох")
void capitalize_normal_returnsCapitalized() {
assertEquals("Hello", StringUtils.capitalize("hello"));
}
@Test
@DisplayName("Хоосон мөр capitalize")
void capitalize_empty_returnsEmpty() {
assertEquals("", StringUtils.capitalize(""));
}
}
Алхам 5: Eclipse дээр тестийг ажиллуулна:
StringUtilsTestкласс дээр баруун товч → Run As → JUnit Test дарна- Бүх тест ногоон (✅) байх ёстой
3.3 Лаб 2: Хил заагийн шинжилгээ — Дүнгийн баталгаажуулалт
Даалгавар: Оюутны дүнг 0-100 хооронд шалгаж, үсгэн үнэлгээ (A-F) буцаах системийг тестлэх.
// ===== GradeCalculator.java =====
public class GradeCalculator {
public String getGrade(int score) {
if (score < 0 || score > 100) {
throw new IllegalArgumentException("Дүн 0-100 хооронд байх ёстой. Оруулсан: " + score);
}
if (score >= 90) return "A"; // 90-100
if (score >= 80) return "B"; // 80-89
if (score >= 70) return "C"; // 70-79
if (score >= 60) return "D"; // 60-69
return "F"; // 0-59
}
public boolean isPassing(int score) {
String grade = getGrade(score);
return !grade.equals("F"); // F биш бол тэнцсэн
}
}
// ===== GradeCalculatorTest.java — Хил заагийн тест =====
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;
class GradeCalculatorTest {
private GradeCalculator calculator;
@BeforeEach
void setUp() {
calculator = new GradeCalculator();
}
// === Хил заагийн тестүүд (Boundary Value Analysis) ===
@Test
@DisplayName("Хилийн утга: 0 → F")
void getGrade_score0_returnsF() {
assertEquals("F", calculator.getGrade(0));
}
@Test
@DisplayName("Хилийн утга: 59 → F")
void getGrade_score59_returnsF() {
assertEquals("F", calculator.getGrade(59));
}
@Test
@DisplayName("Хилийн утга: 60 → D")
void getGrade_score60_returnsD() {
assertEquals("D", calculator.getGrade(60));
}
@Test
@DisplayName("Хилийн утга: 69 → D")
void getGrade_score69_returnsD() {
assertEquals("D", calculator.getGrade(69));
}
@Test
@DisplayName("Хилийн утга: 70 → C")
void getGrade_score70_returnsC() {
assertEquals("C", calculator.getGrade(70));
}
@Test
@DisplayName("Хилийн утга: 79 → C")
void getGrade_score79_returnsC() {
assertEquals("C", calculator.getGrade(79));
}
@Test
@DisplayName("Хилийн утга: 80 → B")
void getGrade_score80_returnsB() {
assertEquals("B", calculator.getGrade(80));
}
@Test
@DisplayName("Хилийн утга: 89 → B")
void getGrade_score89_returnsB() {
assertEquals("B", calculator.getGrade(89));
}
@Test
@DisplayName("Хилийн утга: 90 → A")
void getGrade_score90_returnsA() {
assertEquals("A", calculator.getGrade(90));
}
@Test
@DisplayName("Хилийн утга: 100 → A")
void getGrade_score100_returnsA() {
assertEquals("A", calculator.getGrade(100));
}
// === Exception тестүүд ===
@Test
@DisplayName("Сөрөг дүн → Exception")
void getGrade_negativeScore_throwsException() {
assertThrows(IllegalArgumentException.class, () -> {
calculator.getGrade(-1);
});
}
@Test
@DisplayName("101 дүн → Exception")
void getGrade_score101_throwsException() {
assertThrows(IllegalArgumentException.class, () -> {
calculator.getGrade(101);
});
}
// === Параметрчилсэн тест (олон утгаар нэг дор тестлэх) ===
@ParameterizedTest
@DisplayName("Олон дүнгээр тестлэх")
@CsvSource({
"0, F", "30, F", "59, F",
"60, D", "65, D", "69, D",
"70, C", "75, C", "79, C",
"80, B", "85, B", "89, B",
"90, A", "95, A", "100, A"
})
void getGrade_variousScores_returnsCorrectGrade(int score, String expectedGrade) {
assertEquals(expectedGrade, calculator.getGrade(score));
}
// === isPassing() тест ===
@ParameterizedTest
@DisplayName("Тэнцсэн дүнгүүд")
@ValueSource(ints = {60, 70, 80, 90, 100})
void isPassing_passingScores_returnsTrue(int score) {
assertTrue(calculator.isPassing(score));
}
@ParameterizedTest
@DisplayName("Тэнцээгүй дүнгүүд")
@ValueSource(ints = {0, 30, 59})
void isPassing_failingScores_returnsFalse(int score) {
assertFalse(calculator.isPassing(score));
}
}
3.4 Лаб 3: TDD аргаар Password Validator хөгжүүлэх
Даалгавар: Нууц үгийн хүчтэй байдлыг шалгах PasswordValidator классыг TDD аргаар бичих.
Шаардлага:
- Нууц үг дор хаяж 8 тэмдэгттэй
- Дор хаяж 1 том үсэг
- Дор хаяж 1 жижиг үсэг
- Дор хаяж 1 тоо
- Дор хаяж 1 тусгай тэмдэгт (!@#$%^&*)
Алхам 1 — 🔴 RED: Тестийг эхлээд бичнэ
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;
class PasswordValidatorTest {
private PasswordValidator validator = new PasswordValidator();
@Test
@DisplayName("null нууц үг → false")
void validate_null_returnsFalse() {
assertFalse(validator.isValid(null));
}
@Test
@DisplayName("Хоосон нууц үг → false")
void validate_empty_returnsFalse() {
assertFalse(validator.isValid(""));
}
@Test
@DisplayName("7 тэмдэгт → богино → false")
void validate_tooShort_returnsFalse() {
assertFalse(validator.isValid("Ab1!xyz")); // 7 тэмдэгт
}
@Test
@DisplayName("Том үсэггүй → false")
void validate_noUpperCase_returnsFalse() {
assertFalse(validator.isValid("abcdef1!"));
}
@Test
@DisplayName("Жижиг үсэггүй → false")
void validate_noLowerCase_returnsFalse() {
assertFalse(validator.isValid("ABCDEF1!"));
}
@Test
@DisplayName("Тоогүй → false")
void validate_noDigit_returnsFalse() {
assertFalse(validator.isValid("Abcdefg!"));
}
@Test
@DisplayName("Тусгай тэмдэгтгүй → false")
void validate_noSpecialChar_returnsFalse() {
assertFalse(validator.isValid("Abcdefg1"));
}
@Test
@DisplayName("Бүх шаардлага хангасан → true")
void validate_validPassword_returnsTrue() {
assertTrue(validator.isValid("MyPass1!"));
}
@ParameterizedTest
@DisplayName("Хүчтэй нууц үгүүд")
@ValueSource(strings = {"Hello@123", "Str0ng!Pass", "Test#2024x"})
void validate_strongPasswords_returnTrue(String password) {
assertTrue(validator.isValid(password));
}
}
Алхам 2 — 🟢 GREEN: Тестийг дамжуулах кодыг бичнэ
public class PasswordValidator {
public boolean isValid(String password) {
// null эсвэл хоосон → буруу
if (password == null || password.isEmpty()) {
return false;
}
// 8-аас бага тэмдэгт → буруу
if (password.length() < 8) {
return false;
}
boolean hasUpper = false; // Том үсэг
boolean hasLower = false; // Жижиг үсэг
boolean hasDigit = false; // Тоо
boolean hasSpecial = false; // Тусгай тэмдэгт
for (char c : password.toCharArray()) {
if (Character.isUpperCase(c)) hasUpper = true;
else if (Character.isLowerCase(c)) hasLower = true;
else if (Character.isDigit(c)) hasDigit = true;
else hasSpecial = true;
}
return hasUpper && hasLower && hasDigit && hasSpecial;
}
// Нууц үгийн хүч тооцоолох (нэмэлт)
public String getStrength(String password) {
if (!isValid(password)) return "Сул";
if (password.length() >= 12) return "Хүчтэй";
return "Дунд";
}
}
Алхам 3 — 🔵 REFACTOR: Тест бүгд ногоон → Код цэвэрлэх
Бүх тест ажиллуулж, бүгд ногоон (✅) болсон эсэхийг шалгана. Шаардлагатай бол кодыг цэвэрлэнэ.
3.5 Лаб 4: Mock ашиглах жишээ (Mockito)
Даалгавар: Имэйл илгээх системийг Mock ашиглан тестлэх.
Алхам 1: pom.xml-д Mockito нэмэх:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
Алхам 2: Код бичих:
// ===== EmailService интерфейс =====
interface EmailService {
boolean sendEmail(String to, String subject, String body);
}
// ===== UserRegistration класс =====
class UserRegistration {
private EmailService emailService;
public UserRegistration(EmailService emailService) {
this.emailService = emailService; // Dependency injection
}
public boolean register(String name, String email) {
if (name == null || name.isEmpty()) return false;
if (email == null || !email.contains("@")) return false;
// Бүртгэл амжилттай → Баталгаажуулах имэйл илгээх
String subject = "Бүртгэл амжилттай!";
String body = "Сайн байна уу, " + name + "! Таны бүртгэл амжилттай.";
emailService.sendEmail(email, subject, body);
return true;
}
}
Алхам 3: Mock тест бичих:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class UserRegistrationTest {
@Test
@DisplayName("Зөв мэдээлэлтэй бүртгэл → имэйл илгээгдэнэ")
void register_validUser_sendsEmail() {
// ARRANGE — Mock EmailService үүсгэх
EmailService mockEmail = mock(EmailService.class);
when(mockEmail.sendEmail(anyString(), anyString(), anyString()))
.thenReturn(true);
UserRegistration reg = new UserRegistration(mockEmail);
// ACT — Бүртгэл хийх
boolean result = reg.register("Бат", "bat@email.com");
// ASSERT — Бүртгэл амжилттай
assertTrue(result);
// VERIFY — Имэйл илгээгдсэн эсэхийг шалгах
verify(mockEmail, times(1)).sendEmail(
eq("bat@email.com"), // Зөв имэйл хаяг руу
eq("Бүртгэл амжилттай!"), // Зөв гарчигтай
contains("Бат") // Нэр агуулсан
);
}
@Test
@DisplayName("Буруу имэйлтэй бүртгэл → имэйл илгээгдэхгүй")
void register_invalidEmail_noEmailSent() {
EmailService mockEmail = mock(EmailService.class);
UserRegistration reg = new UserRegistration(mockEmail);
boolean result = reg.register("Бат", "invalid-email");
assertFalse(result);
// Имэйл огт дуудагдаагүй байх ёстой
verify(mockEmail, never()).sendEmail(anyString(), anyString(), anyString());
}
@Test
@DisplayName("Нэр хоосон → бүртгэл амжилтгүй")
void register_emptyName_returnsFalse() {
EmailService mockEmail = mock(EmailService.class);
UserRegistration reg = new UserRegistration(mockEmail);
assertFalse(reg.register("", "bat@email.com"));
assertFalse(reg.register(null, "bat@email.com"));
verify(mockEmail, never()).sendEmail(anyString(), anyString(), anyString());
}
}
---
---