JavaRush /Java блогы /Random-KK /Java Unit Testing: әдістер, түсініктер, тәжірибе
Константин
Деңгей

Java Unit Testing: әдістер, түсініктер, тәжірибе

Топта жарияланған
Бүгін сіз сынақтармен қамтылмаған қосымшаны таба алмайсыз, сондықтан бұл тақырып жаңадан бастаушылар үшін бұрынғыдан да өзекті болады: сынақтарсыз сіз ешқайда жете алмайсыз. Жарнама ретінде өткен мақалаларымды қарауды ұсынамын. Олардың кейбіреулері сынақтарды қамтиды (сонымен бірге мақалалар өте пайдалы болады):
  1. MySql ауыстыру үшін MariaDB көмегімен дерекқорды интеграциялық тестілеу
  2. Көптілді қолданбаны жүзеге асыру
  3. Файлдарды қолданбаға және олар туралы мәліметтерді дерекқорға сақтау
Тестілеудің қандай түрлері негізінен қолданылатынын қарастырайық, содан кейін біз бірлік тестілеу туралы білуіңіз керек барлық нәрсені егжей-тегжейлі зерттейміз.

Тестілеу түрлері

Сынақ дегеніміз не? Wiki айтқандай: « Тест немесе сынақ жүйені әртүрлі жағдайларда орналастыру және ондағы байқалатын өзгерістерді қадағалау арқылы жүйенің негізгі процестерін зерттеу тәсілі». Басқаша айтқанда, бұл белгілі бір жағдайларда біздің жүйенің дұрыс жұмыс істеуінің сынағы. Бірлік тестілеу туралы барлығы: әдістер, түсініктер, тәжірибе – 2Ал, тестілеудің қандай түрлері бар екенін көрейік:
  1. Бірліктерді тестілеу - бұл жүйенің әрбір модулін жеке тестілеу болып табылатын сынақтар. Бұл жүйенің ең аз бөлінетін бөліктері болғаны жөн, мысалы, модульдер.

  2. Жүйелік тестілеу – қолданбаның үлкен бөлігінің немесе тұтас жүйенің жұмысын тексеруге арналған жоғары деңгейлі сынақ.

  3. Регрессиялық тестілеу – жаңа мүмкіндіктер немесе қателерді түзету қолданбаның бар функционалдылығына әсер ететінін және ескі қателердің қайта пайда болатынын тексеру үшін пайдаланылатын сынақ.

  4. Функционалдық тестілеу - бұл қосымшаның бір бөлігінің техникалық сипаттамаларда, пайдаланушы әңгімелерінде және т.б. көрсетілген талаптарға сәйкестігін тексеру.

    Функционалды тестілеу түрлері:

    • «ақ жәшік» жүйенің ішкі іске асырылуын білумен қосымшаның бір бөлігінің талаптарға сәйкестігіне тест;
    • Жүйенің ішкі іске асырылуын білмей, қосымшаның бір бөлігінің талаптарға сәйкестігіне «қара жәшік» сынағы .
  5. Өнімділікті тестілеу - жүйенің немесе оның бір бөлігінің белгілі бір жүктеме кезінде жұмыс істеу жылдамдығын анықтау үшін жазылатын сынақтардың түрі.
  6. Жүктеме сынағы - стандартты жүктемелер кезінде жүйенің тұрақтылығын тексеруге және қолданба дұрыс жұмыс істейтін максималды мүмкін шыңды табуға арналған сынақтар.
  7. Стресс-тестілеу стандартты емес жүктемелер кезінде қолданбаның функционалдығын тексеруге және жүйе бұзылмайтын максималды мүмкін шыңды анықтауға арналған тестілеу түрі болып табылады.
  8. Қауіпсіздікті тестілеу – жүйенің қауіпсіздігін тексеру үшін қолданылатын сынақтар (хакерлердің шабуылдарынан, вирустардан, құпия деректерге рұқсатсыз кіруден және өмірдің басқа ләззаттарынан).
  9. Локализация сынағы – бұл қолданба үшін локализация сынағы.
  10. Қолдану мүмкіндігін тестілеу – пайдаланушылар үшін қолайлылықты, түсініктілігін, тартымдылығын және үйренуге қабілеттілігін тексеруге бағытталған тестілеу түрі.
  11. Мұның бәрі жақсы естіледі, бірақ іс жүзінде бұл қалай жұмыс істейді? Қарапайым: Майк Конның сынақ пирамидасы қолданылады: Бірлік тестілеу туралы барлығы: әдістер, түсініктер, тәжірибе – 4Бұл пирамиданың жеңілдетілген нұсқасы: енді ол кішірек бөліктерге бөлінген. Бірақ бүгін біз бұзбаймыз және ең қарапайым нұсқаны қарастырамыз.
    1. Бірлік – қолданбаның әртүрлі деңгейлерінде қолданылатын бірлік сынақтары, қолданбаның ең кіші бөлінетін логикасын сынау: мысалы, класс, бірақ көбінесе әдіс. Бұл сынақтар әдетте сыртқы логикадан мүмкіндігінше оқшаулауға тырысады, яғни қолданбаның қалған бөлігі стандартты режимде жұмыс істейді деген елес жасауға тырысады.

      Бұл сынақтар әрқашан көп болуы керек (басқа түрлерге қарағанда көбірек), өйткені олар шағын бөліктерді сынайды және өте жеңіл, көп ресурстарды тұтынбайды (ресурстар арқылы мен жедел жады мен уақытты айтамын).

    2. Интеграция – интеграциялық тестілеу. Ол жүйенің үлкен бөліктерін тексереді, яғни бұл не логиканың бірнеше бөліктерінің (бірнеше әдістер немесе сыныптардың) тіркесімі немесе сыртқы компонентпен жұмыс істеудің дұрыстығы. Бірлік сынақтарына қарағанда бұл сынақтар әдетте азырақ, өйткені олар ауырырақ.

      Интеграциялық сынақтардың мысалы ретінде дерекқорға қосылуды және онымен жұмыс істейтін әдістердің дұрыс орындалуын тексеруді қарастыруға болады .

    3. UI – пайдаланушы интерфейсінің жұмысын тексеретін сынақтар. Олар қолданбаның барлық деңгейлерінде логикаға әсер етеді, сондықтан оларды «соңына дейін» деп те атайды. Әдетте, олардың саны аз, өйткені олар ең ауыр салмақты және ең қажетті (пайдаланылған) жолдарды тексеруі керек.

      Жоғарыдағы суретте біз үшбұрыштың әртүрлі бөліктерінің аудандарының қатынасын көреміз: нақты жұмыста осы сынақтардың санында шамамен бірдей пропорция сақталады.

      Бүгін біз ең көп қолданылатын тесттерді - бірлік сынақтарын егжей-тегжейлі қарастырамыз, өйткені барлық өзін-өзі құрметтейтін Java әзірлеушілері оларды негізгі деңгейде пайдалана алуы керек.

    Бірліктерді сынаудың негізгі түсініктері

    Сынақпен қамту (Code Coverage) қолданбалы тестілеу сапасының негізгі бағалауларының бірі болып табылады. Бұл сынақтармен қамтылған codeтың пайызы (0-100%). Іс жүзінде көптеген адамдар бұл пайызды қуады, мен онымен келіспеймін, өйткені олар қажет емес жерлерде сынақтарды қоса бастайды. Мысалы, біздің қызметімізде қосымша логикасыз стандартты CRUD (жасау/алу/жаңарту/жою) операциялары бар. Бұл әдістер жұмысты репозиториймен жұмыс істейтін қабатқа тапсыратын таза делдал болып табылады. Бұл жағдайда бізде тексеретін ештеңе жоқ: мүмкін бұл әдіс Дао әдісін шақырады ма, бірақ бұл маңызды емес. Сынақ қамтуын бағалау үшін әдетте қосымша құралдар қолданылады: JaCoCo, Cobertura, Clover, Emma және т. Бұл мәселені толығырақ зерттеу үшін бірнеше қолайлы мақаланы сақтаңыз: TDD (Test-driven Development) – сынаққа негізделген әзірлеу. Бұл тәсілде, ең алдымен, белгілі бір codeты тексеретін тест жазылады. Бұл қара жәшік сынағы болып шықты: біз кірісте не екенін білеміз және шығыста не болуы керек екенін білеміз. Бұл codeтың қайталануын болдырмайды. Тестке негізделген әзірлеу қолданбаның әрбір шағын функционалдығы үшін сынақтарды әзірлеуден және әзірлеуден басталады. TDD әдісінде, біріншіден, codeтың не істейтінін анықтайтын және тексеретін тест әзірленеді. TDD негізгі мақсаты codeты анық, қарапайым және қатесіз ету болып табылады. Бірлік тестілеу туралы барлығы: әдістер, түсініктер, тәжірибе – 6Әдіс келесі компоненттерден тұрады:
    1. Біз тестімізді жазып жатырмыз.
    2. Біз сынақтан өттік пе, жоқ па (бәрі қызыл екенін көреміз - қорықпаңыз: осылай болуы керек).
    3. Біз осы сынақты қанағаттандыратын codeты қосамыз (сынақты іске қосыңыз).
    4. Біз codeты қайта өңдейміз.
    Бірлік сынақтары тестілеуді автоматтандыру пирамидасының ең кіші элементтері болып табылатындығына сүйене отырып, TDD оларға негізделген. Бірлік тестілерінің көмегімен біз кез келген сыныптың бизнес логикасын тексере аламыз. BDD (Behavior-driven Development) – мінез-құлық арқылы даму. Бұл тәсіл TDD негізделген. Дәлірек айтқанда, әзірлеуге қатысатын әрбір адам үшін жүйенің әрекетін көрсететін түсінікті тілде (әдетте ағылшын тілінде) жазылған мысалдарды пайдаланады. Біз бұл терминді зерттемейміз, өйткені ол негізінен тестерлер мен бизнес-сарапшыларға қатысты. Test Case – сынақтан өтіп жатқан codeтың орындалуын тексеру үшін қажетті қадамдарды, нақты шарттарды және параметрлерді сипаттайтын сценарий. Бекіту - сыналатын әдісті сәтті орындау үшін қажетті тестілеу ортасының күйі. Бұл an objectілердің алдын ала белгіленген жиынтығы және олардың қолданылатын шарттардағы әрекеті.

    Тестілеу кезеңдері

    Тест үш кезеңнен тұрады:
    1. Тексерілетін деректерді көрсету (бекітулер).
    2. Тексеріліп жатқан codeты пайдалану (тексеріліп жатқан әдісті шақыру).
    3. Нәтижелерді тексеру және күтілетін нәтижелермен салыстыру.
    Бірлік тестілеу туралы барлығы: әдістер, түсініктер, тәжірибе – 7Сынақ модульділігін қамтамасыз ету үшін қолданбаның басқа қабаттарынан оқшаулану керек. Мұны түтіктер, мазақ және шпиондар арқылы жасауға болады. Мазақтар - теңшеуге болатын нысандар (мысалы, әрбір сынаққа тән) және біз алуды жоспарлаған жауаптар түріндегі әдіс шақыруларына күтуді орнатуға мүмкіндік береді. Күтуді тексеру Mock нысандарына шақырулар арқылы орындалады. Stubs - тестілеу кезінде қоңырауларға сымды жауап береді. Олар сондай-ақ қоңырау туралы ақпаратты сақтай алады (мысалы, параметрлер немесе осы қоңыраулардың саны). Бұлар кейде өз терминімен аталады - тыңшылық ( Spy ). Кейде бұл stubs және мысқыл терминдері шатастырылады: айырмашылығы - stub ештеңені тексермейді, тек берілген күйді имитациялайды. Мазақ - бұл күтілетін нысан. Мысалы, берілген класс әдісі белгілі бір рет шақырылуы керек. Басқаша айтқанда, сіздің тестіңіз түтікке байланысты ешқашан үзілмейді, бірақ ол мазаққа байланысты үзілуі мүмкін.

    Сынақ орталары

    Ендеше, енді іске кірісейік. Java үшін бірнеше тестілеу орталары (жақтаулар) бар. Олардың ең танымалдары JUnit және TestNG. Біздің шолу үшін біз мыналарды қолданамыз: Бірлік тестілеу туралы барлығы: әдістер, түсініктер, тәжірибе – 8JUnit сынағы – тек тестілеу үшін пайдаланылатын сыныпта қамтылған әдіс. Сынып әдетте соңында +Test арқылы сыналатын сыныппен бірдей аталады. Мысалы, CarService→ CarServiceTest. Maven құрастыру жүйесі сынақ аймағында мұндай сыныптарды автоматты түрде қосады. Шындығында бұл сынып сынақ сыныбы деп аталады. Негізгі annotationларды аздап қарастырайық: @Test - бұл әдісті сынақ әдісі ретінде анықтау (шын мәнінде, бұл annotationмен белгіленген әдіс бірлік сынағы болып табылады). @Before - әрбір сынақ алдында орындалатын әдісті белгілейді. Мысалы, сынып сынақ деректерін толтыру, кіріс деректерін оқу, т.б. @After - әрбір сынақтан кейін шақырылатын әдістің үстіне орналастырылады (деректерді тазалау, әдепкі мәндерді қалпына келтіру). @BeforeClass - әдістің үстіне орналастырылған - @Before аналогы. Бірақ бұл әдіс берілген сынып үшін барлық сынақтар алдында бір рет шақырылады, сондықтан статикалық болуы керек. Ол сынақ деректер базасын көтеру сияқты аса ауыр операцияларды орындау үшін қолданылады. @AfterClass @ BeforeClass-қа қарама-қарсы: берілген сынып үшін бір рет орындалады, бірақ барлық сынақтардан кейін орындалады. Мысалы, тұрақты ресурстарды тазалау немесе дерекқордан ажырату үшін пайдаланылады. @Ignore - төмендегі әдіс өшірілгенін және жалпы сынақтарды орындау кезінде еленбейтінін ескертеді. Ол әртүрлі жағдайларда қолданылады, мысалы, егер негізгі әдіс өзгертілсе және ол үшін сынақты қайталауға уақыт болмаса. Мұндай жағдайларда @Ignore("Кейбір сипаттамалар") сипаттаманы қосқан жөн. @Test (күтілетін = Exception.class) - теріс сынақтар үшін қолданылады. Бұл әдіс қате болған жағдайда қалай әрекет ететінін тексеретін сынақтар, яғни сынақ әдістің кейбір ерекшеліктерді шығаруын күтеді. Мұндай әдіс @Test annotationсымен белгіленеді, бірақ қатесі бар. @Test(timeout=100) - әдіс 100 миллисекундтан аспайтын уақыт ішінде орындалатынын тексереді. @Mock - берілген нысанды мысқыл ретінде орнату үшін сынып өрісте қолданылады (бұл Junit кітапханасынан емес, Mockito-дан) және егер бізге қажет болса, біз белгілі бір жағдайда мазақтың әрекетін орнатамыз. , тікелей сынақ әдісінде. @RunWith(MockitoJUnitRunner.class) - әдіс сыныптың үстіне орналастырылған. Бұл ондағы сынақтарды орындауға арналған түйме. Жүгірушілер әртүрлі болуы мүмкін: мысалы, мыналар бар: MockitoJUnitRunner, JUnitPlatform, SpringRunner және т.б.). JUnit 5 жүйесінде @RunWith annotationсы күштірек @ExtendWith annotationсымен ауыстырылды. Нәтижелерді салыстырудың кейбір әдістерін қарастырайық:
    • assertEquals(Object expecteds, Object actuals)— берілетін an objectілердің тең екендігін тексереді.
    • assertTrue(boolean flag)— берілген мәннің ақиқат екенін тексереді.
    • assertFalse(boolean flag)— берілген мәннің жалған екенін тексереді.
    • assertNull(Object object)– нысанның нөл екенін тексереді.
    • assertSame(Object firstObject, Object secondObject)— берілген мәндердің бір нысанға қатыстылығын тексереді.
    • assertThat(T t, Matcher<T> matcher)— t сәйкестікте көрсетілген шартты қанағаттандыратынын тексереді.
    Сондай-ақ assertj-тен пайдалы салыстыру формасы бар - assertThat(firstObject).isEqualTo(secondObject) Мұнда мен негізгі әдістер туралы айттым, өйткені қалғандары жоғарыда айтылғандардың әртүрлі нұсқалары.

    Тестілеу тәжірибесі

    Енді жоғарыда келтірілген материалды нақты мысал арқылы қарастырайық. Біз қызмет үшін әдісті сынаймыз - жаңарту. Дао қабатын қарастырмаймыз, өйткені ол біздің әдепкі. Сынақтар үшін стартер қосайық:
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
       <version>2.2.2.RELEASE</version>
       <scope>test</scope>
    </dependency>
    Сонымен, қызмет көрсету класы:
    @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 - жаңартылған нысанды дерекқордан тартып алу 9-14 - құрылысшы арқылы нысанды жасау, егер кіріс нысанда өріс болса - оны орнатыңыз, егер жоқ болса - дерекқорда не бар екенін қалдырыңыз және біздің тестке қараңыз:
    @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 — біздің Runner 4 — мысқылды ауыстыру арқылы қызметті дао деңгейінен оқшаулау 11 — сынып үшін сынақ нысанын орнату (біз сынақ хомякы ретінде қолданатын) 22 — біз сынайтын қызмет нысанын орнату
    @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());
    }
    Мұнда біз сынақтың үш бөлікке нақты бөлінуін көреміз: 3-9 - қондырғыларды орнату 11 - сынақтан өткен бөлікті орындау 13-17 - нәтижелерді тексеру Толығырақ: 3-4 - moka dao әрекетін орнату 5 - дананы орнату стандартымыздың үстіне жаңартамыз деп 11 - әдісті қолданыңыз және алынған дананы алыңыз 13 - оның нөл емес екенін тексеріңіз 14 - нәтиже идентификаторын және көрсетілген әдіс аргументтерін тексеріңіз 15 - атау жаңартылғанын тексеріңіз 16 - қарау нәтижесі бойынша cpu 17 - біз оны жаңарту данасы өрісінде орнатпағандықтан, ол өзгеріссіз қалуы керек, оны тексерейік. Бірлік тестілеу туралы барлығы: әдістер, түсініктер, тәжірибе – 9Іске қосайық: Бірлік тестілеу туралы барлығы: әдістер, түсініктер, тәжірибе – 10Тест жасыл, сіз дем шығара аласыз)) Сонымен, қорытындылаймыз: тестілеу codeтың сапасын жақсартады және әзірлеу процесін икемді және сенімді етеді. Жүздеген класс файлдары бар бағдарламалық жасақтаманы қайта құру кезінде қанша күш жұмсау керектігін елестетіп көріңіз. Барлық осы сыныптар үшін бірлік сынақтары жазылғаннан кейін біз сенімді түрде рефактор жасай аламыз. Ең бастысы, ол әзірлеу кезінде қателерді оңай табуға көмектеседі. Балалар, бүгін мен үшін бәрі: лайк басып, пікір жазыңыз))) Бірлік тестілеу туралы барлығы: әдістер, түсініктер, тәжірибе – 11
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION