JavaRush /Java blogi /Random-UZ /Java Unit Testing: texnikalar, tushunchalar, amaliyot

Java Unit Testing: texnikalar, tushunchalar, amaliyot

Guruhda nashr etilgan
Bugun siz testlar bilan qamrab olinmagan dasturni deyarli topa olmaysiz, shuning uchun bu mavzu yangi boshlanuvchilar uchun har qachongidan ham dolzarbroq bo'ladi: testlarsiz siz hech qaerga kira olmaysiz. Reklama sifatida o'tgan maqolalarimni ko'rib chiqishingizni maslahat beraman. Ulardan ba'zilari testlarni qamrab oladi (va shunga qaramay, maqolalar juda foydali bo'ladi):
  1. MySql-ni almashtirish uchun MariaDB-dan foydalangan holda ma'lumotlar bazasini integratsiya sinovi
  2. Ko'p tilli dasturni amalga oshirish
  3. Ilovaga fayllarni va ular haqidagi ma'lumotlarni ma'lumotlar bazasiga saqlash
Keling, printsipial jihatdan qanday sinov turlari qo'llanilishini ko'rib chiqaylik va shundan so'ng biz birlik testi haqida bilishingiz kerak bo'lgan hamma narsani batafsil o'rganamiz.

Sinov turlari

Sinov nima? Wiki aytganidek: " Test yoki test - bu tizimni turli vaziyatlarga joylashtirish va undagi kuzatilishi mumkin bo'lgan o'zgarishlarni kuzatish orqali tizimning asosiy jarayonlarini o'rganish usuli." Boshqacha qilib aytganda, bu tizimimizning muayyan vaziyatlarda to'g'ri ishlashini tekshirishdir. Birlik testi haqida hamma narsa: usullar, tushunchalar, amaliyot - 2Keling, qanday sinov turlari borligini ko'rib chiqaylik:
  1. Birlik testi - bu tizimning har bir modulini individual ravishda sinab ko'rish vazifasi bo'lgan testlar. Bu tizimning minimal bo'linadigan qismlari, masalan, modullar bo'lishi maqsadga muvofiqdir.

  2. Tizim testi - bu dasturning katta qismi yoki butun tizimning ishlashini tekshirish uchun yuqori darajadagi test.

  3. Regressiya testi - bu yangi xususiyatlar yoki xatolarni tuzatish ilovaning mavjud funksiyalariga ta'sir qiladimi yoki yo'qmi va eski xatolar qayta paydo bo'ladimi yoki yo'qligini tekshirish uchun ishlatiladigan test.

  4. Funktsional test - bu dasturning bir qismi spetsifikatsiyalarda, foydalanuvchi hikoyalarida va boshqalarda ko'rsatilgan talablarga muvofiqligini tekshirish.

    Funktsional test turlari:

    • tizimning ichki joriy etilishini bilish bilan ilovaning bir qismining talablarga muvofiqligi uchun "oq quti" testi ;
    • Tizimning ichki joriy etilishini bilmasdan, ilovaning bir qismining talablarga muvofiqligi uchun "qora quti" testi .
  5. Ishlash testi - bu tizim yoki uning bir qismi ma'lum bir yuk ostida ishlash tezligini aniqlash uchun yoziladigan testlar turi.
  6. Yuk testi - standart yuklar ostida tizimning barqarorligini tekshirish va dasturning to'g'ri ishlashi mumkin bo'lgan maksimal cho'qqini topish uchun mo'ljallangan testlar.
  7. Stress testi - bu nostandart yuklar ostida dasturning funksionalligini tekshirish va tizim ishdan chiqmasligi mumkin bo'lgan maksimal cho'qqini aniqlash uchun mo'ljallangan sinov turi.
  8. Xavfsizlik testi - tizim xavfsizligini tekshirish uchun ishlatiladigan testlar (xakerlar, viruslar hujumlaridan, maxfiy ma'lumotlarga ruxsatsiz kirishdan va hayotning boshqa zavqlaridan).
  9. Lokalizatsiya testi - bu dastur uchun lokalizatsiya testi.
  10. Foydalanish testi - bu foydalanuvchilar uchun qulaylik, tushunarlilik, jozibadorlik va o'rganishni tekshirishga qaratilgan test turi.
  11. Bularning barchasi yaxshi eshitiladi, lekin amalda bu qanday ishlaydi? Hammasi oddiy: Mayk Konning sinov piramidasi qo'llaniladi: Birlik testi haqida hamma narsa: usullar, tushunchalar, amaliyot - 4Bu piramidaning soddalashtirilgan versiyasi: endi u kichikroq qismlarga bo'lingan. Ammo bugun biz buzg'unchilik qilmaymiz va eng oddiy variantni ko'rib chiqamiz.
    1. Birlik - ilovaning turli qatlamlarida qo'llaniladigan birlik testlari, ilovaning eng kichik bo'linadigan mantiqini sinab ko'rish: masalan, sinf, lekin ko'pincha usul. Ushbu testlar odatda tashqi mantiqdan imkon qadar ko'proq ajratishga, ya'ni dasturning qolgan qismi standart rejimda ishlayotgani haqidagi illyuziya yaratishga harakat qiladi.

      Ushbu testlar har doim juda ko'p bo'lishi kerak (boshqa turlarga qaraganda ko'proq), chunki ular kichik qismlarni sinab ko'rishadi va juda engil, juda ko'p resurslarni iste'mol qilmaydi (resurslar deganda men RAM va vaqtni nazarda tutaman).

    2. Integratsiya - integratsiya testi. U tizimning kattaroq qismlarini tekshiradi, ya'ni bu mantiqning bir nechta qismlarini (bir nechta usullar yoki sinflar) kombinatsiyasi yoki tashqi komponent bilan ishlashning to'g'riligini tekshiradi. Odatda bu testlar birlik testlariga qaraganda kamroq, chunki ular og'irroqdir.

      Integratsiya testlariga misol sifatida siz ma'lumotlar bazasiga ulanish va u bilan ishlaydigan usullarning to'g'ri bajarilishini tekshirishni ko'rib chiqishingiz mumkin .

    3. UI - foydalanuvchi interfeysi ishlashini tekshiradigan testlar. Ular dasturning barcha darajalarida mantiqqa ta'sir qiladi, shuning uchun ularni oxirigacha deb ham atashadi. Qoida tariqasida, ularning soni kamroq, chunki ular eng og'ir vaznli va eng kerakli (ishlatilgan) yo'llarni tekshirishlari kerak.

      Yuqoridagi rasmda biz uchburchakning turli qismlarining maydonlarining nisbatini ko'ramiz: haqiqiy ishda ushbu testlar sonida taxminan bir xil nisbat saqlanadi.

      Bugun biz eng ko'p ishlatiladigan testlarni - birlik testlarini batafsil ko'rib chiqamiz, chunki barcha o'zini hurmat qiladigan Java dasturchilari ulardan asosiy darajada foydalanishlari kerak.

    Birlik testining asosiy tushunchalari

    Test qamrovi (Code Coverage) dastur sinovi sifatining asosiy baholaridan biridir. Bu testlar bilan qamrab olingan kodning foizi (0-100%). Amalda, ko'p odamlar bu foizni ta'qib qilishadi, men bunga rozi emasman, chunki ular kerak bo'lmagan joylarda testlarni qo'shishni boshlaydilar. Masalan, bizning xizmatimizda qo'shimcha mantiqsiz standart CRUD (yaratish/olish/yangilash/o'chirish) operatsiyalari mavjud. Bu usullar ishni ombor bilan ishlaydigan qatlamga topshiradigan sof vositachilardir. Bunday vaziyatda bizda sinab ko'rish uchun hech narsa yo'q: ehtimol bu usul Tao usulini chaqiradimi, lekin bu jiddiy emas. Sinov qamrovini baholash uchun odatda qo'shimcha vositalar qo'llaniladi: JaCoCo, Cobertura, Clover, Emma va boshqalar. Ushbu masalani batafsil o'rganish uchun bir nechta mos maqolalarni saqlang: TDD (Test-driven Development) - test asosida ishlab chiqish. Ushbu yondashuvda, birinchi navbatda, ma'lum bir kodni tekshiradigan test yoziladi. Bu qora quti sinovi bo'lib chiqdi: biz kirishda nima borligini va chiqishda nima bo'lishini bilamiz. Bu kodni takrorlashdan qochadi. Sinovga asoslangan ishlab chiqish dasturning har bir kichik funksionalligi uchun testlarni loyihalash va ishlab chiqishdan boshlanadi. TDD yondashuvida, birinchi navbatda, kod nima qilishini aniqlaydigan va tekshiradigan test ishlab chiqiladi. TDD ning asosiy maqsadi kodni aniqroq, sodda va xatosiz qilishdir. Birlik testi haqida hamma narsa: usullar, tushunchalar, amaliyot - 6Yondashuv quyidagi tarkibiy qismlardan iborat:
    1. Biz testimizni yozyapmiz.
    2. Biz sinovdan o'tdikmi yoki yo'qmi, uni o'tkazamiz (biz hamma narsa qizil ekanligini ko'ramiz - vahima qilmang: shunday bo'lishi kerak).
    3. Biz ushbu testni qondirishi kerak bo'lgan kodni qo'shamiz (testni bajaring).
    4. Biz kodni qayta tiklaymiz.
    Birlik testlari sinovlarni avtomatlashtirish piramidasining eng kichik elementlari ekanligiga asoslanib, TDD ularga asoslanadi. Birlik testlari yordamida biz har qanday sinfning biznes mantiqini sinab ko'rishimiz mumkin. BDD (Behavior-driven Development) - xatti-harakatlar orqali rivojlanish. Ushbu yondashuv TDDga asoslangan. Aniqroq qilib aytganda, u aniq tilda (odatda ingliz tilida) yozilgan misollardan foydalanadi, bu esa rivojlanishda ishtirok etgan har bir kishi uchun tizimning xatti-harakatlarini tasvirlaydi. Biz bu atamaga chuqurroq kirmaymiz, chunki u asosan testerlar va biznes tahlilchilarga ta'sir qiladi. Test ishi - sinov ostidagi kodning bajarilishini tekshirish uchun zarur bo'lgan qadamlar, aniq shartlar va parametrlarni tavsiflovchi skript. Fikstura - bu sinovdan o'tayotgan usulni muvaffaqiyatli bajarish uchun zarur bo'lgan sinov muhitining holati. Bu ob'ektlarning oldindan belgilangan to'plami va foydalanilgan sharoitlarda ularning xatti-harakatlari.

    Sinov bosqichlari

    Sinov uch bosqichdan iborat:
    1. Sinov qilinadigan ma'lumotlarni ko'rsatish (fiksatorlar).
    2. Sinov ostidagi koddan foydalanish (sinov ostidagi usulni chaqirish).
    3. Natijalarni tekshirish va ularni kutilganlar bilan solishtirish.
    Birlik testi haqida hamma narsa: usullar, tushunchalar, amaliyot - 7Sinov modulligini ta'minlash uchun siz dasturning boshqa qatlamlaridan ajratilgan bo'lishingiz kerak. Bu stublar, masxaralar va ayg'oqchilar yordamida amalga oshirilishi mumkin. Masxara - sozlanishi mumkin bo'lgan ob'ektlar (masalan, har bir test uchun maxsus) va biz olishni rejalashtirgan javoblar ko'rinishidagi usul chaqiruvlari uchun taxminlarni belgilash imkonini beradi. Kutish tekshiruvlari Mock obyektlariga qo'ng'iroqlar orqali amalga oshiriladi. Stublar - sinov paytida qo'ng'iroqlarga qattiq simli javobni ta'minlaydi. Shuningdek, ular qo'ng'iroq haqidagi ma'lumotlarni (masalan, parametrlar yoki ushbu qo'ng'iroqlar soni) saqlashi mumkin. Bularni ba'zan o'z atamasi bilan atashadi - ayg'oqchi ( Spy ). Ba'zida bu stub va masxara atamalari chalkashib ketadi: farq shundaki, stub hech narsani tekshirmaydi, faqat berilgan holatni taqlid qiladi. Masxara - bu kutilgan narsadir. Masalan, berilgan sinf usuli ma'lum bir necha marta chaqirilishi kerak. Boshqacha qilib aytadigan bo'lsak, sizning testingiz hech qachon stub tufayli buzilmaydi, lekin u masxara tufayli buzilishi mumkin.

    Sinov muhitlari

    Shunday qilib, keling, biznesga o'taylik. Java uchun bir nechta sinov muhitlari (ramkalar) mavjud. Ulardan eng mashhurlari JUnit va TestNG. Ko'rib chiqish uchun biz foydalanamiz: Birlik testi haqida hamma narsa: usullar, tushunchalar, amaliyot - 8JUnit testi bu sinfda mavjud bo'lgan usul bo'lib, u faqat sinov uchun ishlatiladi. Sinf odatda sinovdan o'tayotgan sinf bilan bir xil nomlanadi, oxirida +Test. Masalan, CarService→ CarServiceTest. Maven qurish tizimi avtomatik ravishda bunday sinflarni sinov maydoniga kiritadi. Aslida, bu sinf test sinfi deb ataladi. Keling, asosiy izohlarni biroz ko'rib chiqaylik: @Test - bu usulning test usuli sifatida ta'rifi (aslida, bu izoh bilan belgilangan usul birlik testidir). @Before - har bir testdan oldin bajariladigan usulni belgilaydi. Misol uchun, sinf test ma'lumotlarini to'ldirish, kirish ma'lumotlarini o'qish va hokazo. @After - har bir testdan so'ng chaqiriladigan usulning tepasida joylashgan (ma'lumotlarni tozalash, standart qiymatlarni tiklash). @BeforeClass - usulning yuqorisida joylashgan - @Before ga o'xshash. Ammo bu usul ma'lum bir sinf uchun barcha testlardan oldin faqat bir marta chaqiriladi va shuning uchun statik bo'lishi kerak. U sinov bazasini ko'tarish kabi og'irroq operatsiyalarni bajarish uchun ishlatiladi. @AfterClass @BeforeClass ga qarama-qarshidir: berilgan sinf uchun bir marta bajariladi, lekin barcha testlardan so'ng bajariladi. Masalan, doimiy resurslarni tozalash yoki ma'lumotlar bazasidan uzish uchun ishlatiladi. @Ignore - quyidagi usul o'chirilganligini va umumiy testlarni o'tkazishda e'tiborga olinmasligini ta'kidlaydi. U turli holatlarda qo'llaniladi, masalan, agar asosiy usul o'zgartirilgan bo'lsa va buning uchun testni qayta o'tkazishga vaqt bo'lmasa. Bunday hollarda tavsif qo'shish ham tavsiya etiladi - @Ignore("Ba'zi tavsif"). @Test (kutilgan = Exception.class) - salbiy testlar uchun ishlatiladi. Bu xatolik yuz berganda usul qanday harakat qilishini tekshiradigan testlardir, ya'ni test usuldan ba'zi istisnolarni keltirib chiqarishini kutadi. Bunday usul @Test izohi bilan belgilanadi, lekin qo'lga olish xatosi bilan. @Test(timeout=100) - usul 100 millisekunddan ko'p bo'lmagan muddatda bajarilishini tekshiradi. @Mock - sinf berilgan ob'ektni masxara sifatida o'rnatish uchun maydonda ishlatiladi (bu Junit kutubxonasidan emas, balki Mockito'dan) va agar bizga kerak bo'lsa, biz masxara harakatini muayyan vaziyatda o'rnatamiz. , to'g'ridan-to'g'ri test usulida. @RunWith(MockitoJUnitRunner.class) - usul sinfdan yuqorida joylashgan. Bu undagi testlarni bajarish tugmasi. Yuguruvchilar har xil bo'lishi mumkin: masalan, quyidagilar mavjud: MockitoJUnitRunner, JUnitPlatform, SpringRunner va boshqalar). JUnit 5 da @RunWith izohi kuchliroq @ExtendWith izohi bilan almashtirildi. Keling, natijalarni taqqoslashning ba'zi usullarini ko'rib chiqaylik:
    • assertEquals(Object expecteds, Object actuals)— uzatiladigan ob'ektlar teng yoki yo'qligini tekshiradi.
    • assertTrue(boolean flag)— o'tkazilgan qiymat rostligini qaytaradimi yoki yo'qligini tekshiradi.
    • assertFalse(boolean flag)— o'tkazilgan qiymat noto'g'ri ekanligini tekshiradi.
    • assertNull(Object object)– obyektning null ekanligini tekshiradi.
    • assertSame(Object firstObject, Object secondObject)- o'tkazilgan qiymatlar bir xil ob'ektga tegishli yoki yo'qligini tekshiradi.
    • assertThat(T t, Matcher<T> matcher)— t mos keluvchida ko'rsatilgan shartga javob berishini tekshiradi.
    Assertj dan foydali taqqoslash shakli ham mavjud - assertThat(firstObject).isEqualTo(secondObject) Bu erda men asosiy usullar haqida gapirdim, chunki qolganlari yuqoridagilarning turli xil variantlari.

    Sinov amaliyoti

    Endi yuqoridagi materialni aniq bir misol yordamida ko'rib chiqamiz. Xizmat uchun usulni sinab ko'ramiz - yangilash. Biz dao qatlamini ko'rib chiqmaymiz, chunki u bizning standartimiz. Sinovlar uchun boshlang'ich qo'shamiz:
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
       <version>2.2.2.RELEASE</version>
       <scope>test</scope>
    </dependency>
    Shunday qilib, xizmat ko'rsatish sinfi:
    @Service
    @RequiredArgsConstructor
    public class RobotServiceImpl implements RobotService {
       private final RobotDAO robotDAO;
    
       @Override
       public Robot update(Long id, Robot robot) {
           Robot found = robotDAO.findById(id);
           return robotDAO.update(Robot.builder()
                   .id(id)
                   .name(robot.getName() != null ? robot.getName() : found.getName())
                   .cpu(robot.getCpu() != null ? robot.getCpu() : found.getCpu())
                   .producer(robot.getProducer() != null ? robot.getProducer() : found.getProducer())
                   .build());
       }
    }
    8 - yangilangan ob'ektni ma'lumotlar bazasidan tortib oling 9-14 - ob'ektni quruvchi orqali yarating, agar kiruvchi ob'ektda maydon bo'lsa - uni o'rnating, agar bo'lmasa - ma'lumotlar bazasida nima borligini qoldiring va testimizga qarang:
    @RunWith(MockitoJUnitRunner.class)
    public class RobotServiceImplTest {
       @Mock
       private RobotDAO robotDAO;
    
       private RobotServiceImpl robotService;
    
       private static Robot testRobot;
    
       @BeforeClass
       public static void prepareTestData() {
           testRobot = Robot
                   .builder()
                   .id(123L)
                   .name("testRobotMolly")
                   .cpu("Intel Core i7-9700K")
                   .producer("China")
                   .build();
       }
    
       @Before
       public void init() {
           robotService = new RobotServiceImpl(robotDAO);
       }
    1 - bizning Runner 4 - soxtani almashtirish orqali xizmatni dao qatlamidan ajratib oling 11 - sinf uchun sinov ob'ektini o'rnating (biz sinov hamsteri sifatida foydalanamiz) 22 - biz sinab ko'radigan xizmat ob'ektini o'rnating
    @Test
    public void updateTest() {
       when(robotDAO.findById(any(Long.class))).thenReturn(testRobot);
       when(robotDAO.update(any(Robot.class))).then(returnsFirstArg());
       Robot robotForUpdate = Robot
               .builder()
               .name("Vally")
               .cpu("AMD Ryzen 7 2700X")
               .build();
    
       Robot resultRobot = robotService.update(123L, robotForUpdate);
    
       assertNotNull(resultRobot);
       assertSame(resultRobot.getId(),testRobot.getId());
       assertThat(resultRobot.getName()).isEqualTo(robotForUpdate.getName());
       assertTrue(resultRobot.getCpu().equals(robotForUpdate.getCpu()));
       assertEquals(resultRobot.getProducer(),testRobot.getProducer());
    }
    Bu erda biz testning aniq uch qismga bo'linishini ko'ramiz: 3-9 - moslamalarni o'rnatish 11 - sinovdan o'tgan qismni bajarish 13-17 - natijalarni tekshirish Batafsilroq: 3-4 - moka dao uchun xatti-harakatlarni sozlash 5 - namunani o'rnatish standartimiz ustiga yangilaymiz 11 - usuldan foydalaning va natijada olingan misolni oling 13 - nol emasligini tekshiring 14 - natija identifikatorini va belgilangan usul argumentlarini tekshiring 15 - nom yangilanganligini tekshiring 16 - qarang natijada cpu 17 - biz buni yangilash namunasi maydoniga o'rnatmaganimiz sababli, u bir xil bo'lib qolishi kerak, keling, tekshiramiz. Birlik testi haqida hamma narsa: usullar, tushunchalar, amaliyot - 9Keling, ishga tushamiz: Birlik testi haqida hamma narsa: usullar, tushunchalar, amaliyot - 10Test yashil rangda, siz nafas olishingiz mumkin)) Xullas, xulosa qilaylik: test kod sifatini yaxshilaydi va ishlab chiqish jarayonini yanada moslashuvchan va ishonchli qiladi. Yuzlab sinf fayllari bilan dasturiy ta'minotni qayta loyihalashda qancha kuch sarflashimiz kerakligini tasavvur qiling. Ushbu sinflarning barchasi uchun birlik testlari yozilgan bo'lsa, biz ishonch bilan qayta ishlashimiz mumkin. Va eng muhimi, rivojlanish jarayonida xatolarni osongina topishga yordam beradi. Bolalar, bugun hammasi men uchun: layk qo'ying, sharhlar yozing))) Birlik testi haqida hamma narsa: usullar, tushunchalar, amaliyot - 11
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION