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.

🕑 13 daqiqa o‘qish 📄 780 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Eng oddiy sinov vositasi
  2. Test yiqilganda
  3. Arrange - Act - Assert
  4. Chegaraviy holatlar
  5. Istisno tashlanishini tekshirish
  6. Haqiqiy loyihada - JUnit 5
  7. assert kalit so'zi
  8. Nosozlikni qidirish
  9. Xulosa

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:

Java
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);
    }
}
Natija
  OK    2 ning kvadrati
  OK    0 ning kvadrati
  OK    manfiy sonning kvadrati
  OK    kvadrat musbat

O'tdi: 4, yiqildi: 0
Objects.equals nega ishlatildi

Oddiy 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 #

Java
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);
    }
}
Natija
  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:

Java
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;
    }
}
Natija
OK

Chegaraviy holatlar #

Eng ko'p xato chegaralarda bo'ladi:

Java
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);
    }
}
Natija
  OK    bo'sh satr
  OK    aynan chegarada
  OK    chegaradan uzun
  OK    bitta belgi

O'tdi: 4, yiqildi: 0
TurTekshiriladigan holatlar
Sonlar0, manfiy, Integer.MAX_VALUE, chegaradan bir kam va bir ko'p
SatrlarBo'sh, faqat bo'shliq, null, juda uzun
To'plamlarBo'sh, bitta element, takrorlanuvchi elementlar
Havolalarnull
SanaKabisa yili, oy oxiri
null ni har doim tekshiring

Java'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 #

Java
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);
    }
}
Natija
  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:

XML
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.11.0</version>
    <scope>test</scope>
</dependency>

Test shunday ko'rinadi:

Java
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:

Terminal
mvn test
ElementNima qiladi
@TestOddiy test
@DisplayNameHisobotda ko'rinadigan nom
@ParameterizedTest + @CsvSourceBir test, turli ma'lumot bilan
@BeforeEachHar testdan oldin bajariladi
assertEqualsTenglikni tekshiradi
assertThrowsIstisno tashlanishini tekshiradi
assertAllBir nechta tekshiruvni birga bajaradi
assertEquals da tartib muhim
Java
assertEquals(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:

Natija
expected: <9> but was: <10>

Bu yerda 9 haqiqatan kutilgan qiymatmi yoki siz argumentlarni almashtirib qo'yganmisiz - bilib bo'lmaydi.

Test nomini uch qismdan tuzing

metod_sharoit_kutilganNatija ko'rinishida:

  • qoshish_ikkiMusbatSon_togriNatija
  • bolish_nolga_istisnoTashlaydi
  • qidir_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:

Java
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);
    }
}
Natija
Bu qator bajarildi, chunki assert o'chirilgan
Yosh: -5
assert sukut bo'yicha o'chirilgan

Yuqoridagi assert shart buzilgan bo'lsa ham ishlamadi. Sababi: JVM'da tasdiqlashlar sukut bo'yicha o'chirilgan.

Ularni yoqish uchun -ea bayrog'i kerak:

Terminal
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.

MaqsadVosita
Metod argumentini tekshirishif + IllegalArgumentException
null tekshiruviObjects.requireNonNull
Test yozishJUnit
"Bu hech qachon bo'lmasligi kerak"assert (ixtiyoriy)

Nosozlikni qidirish #

UsulQachon
To'xtash nuqtasi (IDE)Eng kuchli usul - qiymatlarni ko'rish
System.out.printlnTez, oddiy, har joyda ishlaydi
Stek iziIstisno qayerdan boshlangani
Jurnal (java.util.logging, SLF4J)Ishlab turgan tizimda
Java
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);
    }
}
Natija
Xabar: hisob bajarilmadi
Stek chuqurligi: 2
Eng yuqori metod: birinchi
Sababi: / by zero
Nosozlikni topishning izchil usuli
  1. Xato xabarini to'liq o'qing - unda ko'pincha javob bor;
  2. Stek izining birinchi qatoriga qarang - u sizning kodingiz;
  3. getCause() ni tekshiring - asl sabab shu yerda;
  4. Nima o'zgarganini eslang - oxirgi ishlagan holatdan beri;
  5. Muammoni kichraytiring - eng kichik takrorlanadigan misol yarating.

Beshinchi qadam eng kuchlisi: muammoni o'n qatorgacha qisqartirsangiz, sabab odatda o'z-o'zidan ko'rinadi.

Amaliy topshiriq
  1. O'z sinov vositangizni yozib, uchta testdan o'tkazing.
  2. Ataylab noto'g'ri test yozib, xabarni ko'ring.
  3. Arrange-Act-Assert tuzilmasida test yozing.
  4. qisqartir metodi uchun chegaraviy holatlarni tekshiring.
  5. null bilan test qo'shing.
  6. Istisno tashlanishini tekshiruvchi yordamchi yozing.
  7. Maven loyihasiga JUnit 5 ni qo'shing.
  8. @ParameterizedTest bilan bir nechta holatni sinang.
  9. assert ni -ea bilan va usiz ishga tushiring.
  10. 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.
  • assertEquals da birinchi argument - kutilgan qiymat.
  • assert kalit 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.

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.