19-bo‘lim
Testlar va nosozliklarni topish
Nega test kerak, o'z sinov vositangizni yozish, chegaraviy holatlar, JUnit 5 bilan tanishuv, assert kalit so'zi va nosozlikni qidirish usullari.
Ushbu bo‘lim mundarijasi
Test - kodingiz kutilganidek ishlashini avtomatik tekshiradigan kod. U bir marta yoziladi va har o'zgarishdan keyin qayta ishlatiladi.
Eng oddiy sinov vositasi #
Kutubxona o'rnatmasdan ham boshlash mumkin:
public class Sinov {
private static int otdi = 0;
private static int yiqildi = 0;
public static void main(String[] args) {
teng(4, kvadrat(2), "2 ning kvadrati");
teng(0, kvadrat(0), "0 ning kvadrati");
teng(9, kvadrat(-3), "manfiy sonning kvadrati");
rost(kvadrat(5) > 0, "kvadrat musbat");
yakun();
}
static int kvadrat(int son) {
return son * son;
}
static void teng(Object kutilgan, Object haqiqiy, String nom) {
if (java.util.Objects.equals(kutilgan, haqiqiy)) {
otdi++;
System.out.println(" OK " + nom);
} else {
yiqildi++;
System.out.println(" XATO " + nom
+ ": kutilgan " + kutilgan + ", haqiqiy " + haqiqiy);
}
}
static void rost(boolean shart, String nom) {
teng(true, shart, nom);
}
static void yakun() {
System.out.println();
System.out.println("O'tdi: " + otdi + ", yiqildi: " + yiqildi);
}
}
OK 2 ning kvadrati
OK 0 ning kvadrati
OK manfiy sonning kvadrati
OK kvadrat musbat
O'tdi: 4, yiqildi: 0
Objects.equals nega ishlatildiOddiy kutilgan.equals(haqiqiy) yozsak, kutilgan null bo'lganda
sinov vositasining o'zi qulardi.
Objects.equals ikkala tomon null bo'lishini ham to'g'ri hal
qiladi (17-bo'lim). Sinov vositasi hech qachon qulamasligi kerak -
u xatoni ko'rsatishi kerak.
Test yiqilganda #
public class Yiqilish {
private static int otdi = 0;
private static int yiqildi = 0;
public static void main(String[] args) {
teng(10, qoshish(5, 5), "to'g'ri holat");
teng(10, qoshish(5, 4), "ataylab noto'g'ri");
yakun();
}
static int qoshish(int a, int b) {
return a + b;
}
static void teng(Object kutilgan, Object haqiqiy, String nom) {
if (java.util.Objects.equals(kutilgan, haqiqiy)) {
otdi++;
System.out.println(" OK " + nom);
} else {
yiqildi++;
System.out.println(" XATO " + nom
+ ": kutilgan " + kutilgan + ", haqiqiy " + haqiqiy);
}
}
static void yakun() {
System.out.println();
System.out.println("O'tdi: " + otdi + ", yiqildi: " + yiqildi);
}
}
OK to'g'ri holat
XATO ataylab noto'g'ri: kutilgan 10, haqiqiy 9
O'tdi: 1, yiqildi: 1
Yiqilgan test nima kutilgani va nima chiqqanini aytadi - shu tufayli muammoni topish oson.
Diqqat qiling: bitta test yiqilgani bilan qolganlari bajarilishda davom etdi. Yaxshi sinov vositasi birinchi xatoda to'xtamaydi - u hamma muammoni bir yo'la ko'rsatadi.
Arrange - Act - Assert #
Har bir test uch qismdan iborat bo'lishi kerak:
import java.util.List;
public class UchQism {
public static void main(String[] args) {
// Arrange - shart-sharoit tayyorlanadi
Savat savat = new Savat();
savat.qosh(4000);
savat.qosh(12000);
// Act - tekshiriladigan amal bajariladi
long jami = savat.jami();
// Assert - natija tekshiriladi
System.out.println(jami == 16000 ? "OK" : "XATO: " + jami);
}
}
class Savat {
private final List<Long> narxlar = new java.util.ArrayList<>();
void qosh(long narx) {
narxlar.add(narx);
}
long jami() {
long natija = 0;
for (long n : narxlar) {
natija += n;
}
return natija;
}
}
OK
Chegaraviy holatlar #
Eng ko'p xato chegaralarda bo'ladi:
public class Chegara {
private static int otdi = 0;
private static int yiqildi = 0;
public static void main(String[] args) {
teng("", qisqartir("", 5), "bo'sh satr");
teng("salom", qisqartir("salom", 5), "aynan chegarada");
teng("salom...", qisqartir("salomlar", 5), "chegaradan uzun");
teng("a", qisqartir("a", 5), "bitta belgi");
yakun();
}
static String qisqartir(String matn, int uzunlik) {
return matn.length() <= uzunlik
? matn
: matn.substring(0, uzunlik) + "...";
}
static void teng(Object kutilgan, Object haqiqiy, String nom) {
if (java.util.Objects.equals(kutilgan, haqiqiy)) {
otdi++;
System.out.println(" OK " + nom);
} else {
yiqildi++;
System.out.println(" XATO " + nom
+ ": '" + kutilgan + "' != '" + haqiqiy + "'");
}
}
static void yakun() {
System.out.println();
System.out.println("O'tdi: " + otdi + ", yiqildi: " + yiqildi);
}
}
OK bo'sh satr
OK aynan chegarada
OK chegaradan uzun
OK bitta belgi
O'tdi: 4, yiqildi: 0
| Tur | Tekshiriladigan holatlar |
|---|---|
| Sonlar | 0, manfiy, Integer.MAX_VALUE, chegaradan bir kam va bir ko'p |
| Satrlar | Bo'sh, faqat bo'shliq, null, juda uzun |
| To'plamlar | Bo'sh, bitta element, takrorlanuvchi elementlar |
| Havolalar | null |
| Sana | Kabisa yili, oy oxiri |
null ni har doim tekshiringJava'da null eng ko'p uchraydigan qulash sababi (17-bo'lim).
Shuning uchun har bir ommaviy metod uchun null bilan test
yozing.
Ko'pincha shunda ma'lum bo'ladi: metod null da nima qilishi
kerakligi umuman o'ylanmagan. Bu testning eng qimmatli foydasi -
u kodni yozishdan oldin savol berishga majbur qiladi.
Istisno tashlanishini tekshirish #
public class IstisnoSinov {
private static int otdi = 0;
private static int yiqildi = 0;
public static void main(String[] args) {
istisnoTashlaydi(IllegalArgumentException.class,
() -> bolish(10, 0), "nolga bo'lish");
istisnoTashlaydi(IllegalArgumentException.class,
() -> bolish(10, 2), "oddiy bo'lish");
yakun();
}
static int bolish(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("nolga bo'lib bo'lmaydi");
}
return a / b;
}
static void istisnoTashlaydi(Class<? extends Exception> kutilgan,
Runnable amal, String nom) {
try {
amal.run();
yiqildi++;
System.out.println(" XATO " + nom + ": istisno tashlanmadi");
} catch (Exception x) {
if (kutilgan.isInstance(x)) {
otdi++;
System.out.println(" OK " + nom);
} else {
yiqildi++;
System.out.println(" XATO " + nom + ": kutilgan "
+ kutilgan.getSimpleName() + ", haqiqiy "
+ x.getClass().getSimpleName());
}
}
}
static void yakun() {
System.out.println();
System.out.println("O'tdi: " + otdi + ", yiqildi: " + yiqildi);
}
}
OK nolga bo'lish
XATO oddiy bo'lish: istisno tashlanmadi
O'tdi: 1, yiqildi: 1
Haqiqiy loyihada - JUnit 5 #
Amalda o'z vositangizni yozmaysiz. Java dunyosidagi standart - JUnit 5.
Maven loyihasida pom.xml ga qo'shiladi:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
Test shunday ko'rinadi:
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.*;
class KalkulyatorTest {
@Test
@DisplayName("Ikki musbat sonni qo'shish")
void qoshish_ikkiMusbatSon_togriNatija() {
Kalkulyator k = new Kalkulyator();
int natija = k.qoshish(2, 3);
assertEquals(5, natija);
}
@ParameterizedTest
@CsvSource({"0, 0, 0", "1, 1, 2", "-5, 5, 0"})
void qoshish_turliQiymatlar(int a, int b, int kutilgan) {
assertEquals(kutilgan, new Kalkulyator().qoshish(a, b));
}
@Test
void bolish_nolga_istisnoTashlaydi() {
Kalkulyator k = new Kalkulyator();
var x = assertThrows(IllegalArgumentException.class,
() -> k.bolish(10, 0));
assertTrue(x.getMessage().contains("nol"));
}
}
Ishga tushirish:
mvn test
| Element | Nima qiladi |
|---|---|
@Test | Oddiy test |
@DisplayName | Hisobotda ko'rinadigan nom |
@ParameterizedTest + @CsvSource | Bir test, turli ma'lumot bilan |
@BeforeEach | Har testdan oldin bajariladi |
assertEquals | Tenglikni tekshiradi |
assertThrows | Istisno tashlanishini tekshiradi |
assertAll | Bir nechta tekshiruvni birga bajaradi |
assertEquals da tartib muhimassertEquals(kutilgan, haqiqiy); // TO'G'RI
assertEquals(haqiqiy, kutilgan); // teskari
Birinchi argument - kutilgan, ikkinchisi - haqiqiy. Teskari yozsangiz, test baribir ishlaydi, lekin yiqilgan testning xabari chalg'ituvchi bo'ladi:
expected: <9> but was: <10>
Bu yerda 9 haqiqatan kutilgan qiymatmi yoki siz argumentlarni
almashtirib qo'yganmisiz - bilib bo'lmaydi.
metod_sharoit_kutilganNatija ko'rinishida:
qoshish_ikkiMusbatSon_togriNatijabolish_nolga_istisnoTashlaydiqidir_royxatBosh_bosh Optional
Shunda test yiqilganda nomning o'zidan nima buzilganini tushunasiz - kodga qarash shart emas.
assert kalit so'zi #
Java'da assert kalit so'zi ham bor, lekin u testlar uchun emas:
public class Assert {
public static void main(String[] args) {
int yosh = -5;
assert yosh >= 0 : "yosh manfiy bo'lmasin";
System.out.println("Bu qator bajarildi, chunki assert o'chirilgan");
System.out.println("Yosh: " + yosh);
}
}
Bu qator bajarildi, chunki assert o'chirilgan
Yosh: -5
assert sukut bo'yicha o'chirilganYuqoridagi assert shart buzilgan bo'lsa ham ishlamadi. Sababi:
JVM'da tasdiqlashlar sukut bo'yicha o'chirilgan.
Ularni yoqish uchun -ea bayrog'i kerak:
java -ea Assert
Shundan muhim xulosa chiqadi: assert ni ma'lumotni tekshirish
uchun ishlatmang. Foydalanuvchi kiritgan qiymatni yoki metod
argumentini assert bilan tekshirsangiz, tayyor dasturda bu tekshiruv
umuman bo'lmaydi.
| Maqsad | Vosita |
|---|---|
| Metod argumentini tekshirish | if + IllegalArgumentException |
null tekshiruvi | Objects.requireNonNull |
| Test yozish | JUnit |
| "Bu hech qachon bo'lmasligi kerak" | assert (ixtiyoriy) |
Nosozlikni qidirish #
| Usul | Qachon |
|---|---|
| To'xtash nuqtasi (IDE) | Eng kuchli usul - qiymatlarni ko'rish |
System.out.println | Tez, oddiy, har joyda ishlaydi |
| Stek izi | Istisno qayerdan boshlangani |
Jurnal (java.util.logging, SLF4J) | Ishlab turgan tizimda |
public class StekIzi {
public static void main(String[] args) {
try {
birinchi();
} catch (IllegalStateException x) {
System.out.println("Xabar: " + x.getMessage());
StackTraceElement[] iz = x.getStackTrace();
System.out.println("Stek chuqurligi: " + iz.length);
System.out.println("Eng yuqori metod: " + iz[0].getMethodName());
System.out.println("Sababi: " + x.getCause().getMessage());
}
}
static void birinchi() {
try {
ikkinchi();
} catch (ArithmeticException x) {
throw new IllegalStateException("hisob bajarilmadi", x);
}
}
static void ikkinchi() {
int a = 1 / 0;
System.out.println(a);
}
}
Xabar: hisob bajarilmadi
Stek chuqurligi: 2
Eng yuqori metod: birinchi
Sababi: / by zero
- Xato xabarini to'liq o'qing - unda ko'pincha javob bor;
- Stek izining birinchi qatoriga qarang - u sizning kodingiz;
getCause()ni tekshiring - asl sabab shu yerda;- Nima o'zgarganini eslang - oxirgi ishlagan holatdan beri;
- Muammoni kichraytiring - eng kichik takrorlanadigan misol yarating.
Beshinchi qadam eng kuchlisi: muammoni o'n qatorgacha qisqartirsangiz, sabab odatda o'z-o'zidan ko'rinadi.
- O'z sinov vositangizni yozib, uchta testdan o'tkazing.
- Ataylab noto'g'ri test yozib, xabarni ko'ring.
- Arrange-Act-Assert tuzilmasida test yozing.
qisqartirmetodi uchun chegaraviy holatlarni tekshiring.nullbilan test qo'shing.- Istisno tashlanishini tekshiruvchi yordamchi yozing.
- Maven loyihasiga JUnit 5 ni qo'shing.
@ParameterizedTestbilan bir nechta holatni sinang.assertni-eabilan va usiz ishga tushiring.- Istisnoni sabab bilan qayta tashlab,
getCause()ni o'qing.
Xulosa #
- Test bir marta yoziladi, har o'zgarishdan keyin ishlaydi.
- Sinov vositasi birinchi xatoda to'xtamasligi kerak.
- Har bir test Arrange - Act - Assert tuzilmasida bo'lsin.
- Eng ko'p xato chegaralarda: bo'sh, nol, bitta element,
null. - Java'da standart vosita - JUnit 5:
@Test,@ParameterizedTest,assertEquals,assertThrows. assertEqualsda birinchi argument - kutilgan qiymat.assertkalit so'zi sukut bo'yicha o'chirilgan - uni ma'lumot tekshirish uchun ishlatmang.- Nosozlikni topishda muammoni kichraytirish eng samarali usul.
Keyingi va oxirgi bo'limda hamma o'rganganimizni bitta loyihada birlashtiramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.