Създавайте приложения за Android без грешки

Поддържаемостта е една от целите ни при кодиране. Едно от предимствата на модулното тестване е да направи кода по-поддържаем, моля, проверете предимствата тук.

Въпреки това, когато имаме лош код в модулните тестове и трябва да променим съществуваща функция, това ще изисква извършване на промени в производствения код, както и в модулните тестове (с техния лош код).

Единичните тестове биха били като ад или капан, защото щяхме да прекараме много време в четене и опити да разберем какво точно е написано, вместо да ни помогнат, като направим кода поддържаем!

С други думи, ако искаме модулните тестове да направят кода по-поддържаем, трябва да направим модулните тестове възможно най-чисти. в противен случай малка промяна ще бъде голям проблем.

Тестовият код е също толкова важен, колкото производственият код. Не е гражданин втора класа. Изисква мисъл, дизайн и грижа. Трябва да се поддържа чист като производствения код.

— Робърт С. Мартин, Чист код: Наръчник за гъвкав софтуер

Избор на име на тестов метод

Една от най-популярните насоки, които правят кода чист, е да изберете добро име за методи и променливи. можете да следвате тези съвети, за да изберете добро име за тестови методи за вашия проект:

Четивност

Четивността се постига чрез избора на просто, изразително и смислено име.

Една конвенция за именуване

По-добре е да използвате една конвенция за именуване във вашия проект, което прави четенето на тестови отчети по-разбираемо и последователно.

В някои случаи обаче можете да използвате друга конвенция, ако сте в друг слой.

Например някои разработчици не харесват тази конвенция:

MethodName_StateUnderTest_ExpectedBehavior

Това е така, защото съдържа име на метод и техния аргумент, че unit test трябва да тества поведението, а не кода. Това означава, че тестовият код не трябва да влияе чрез промяна на производствения код. Тази конвенция тясно свързва тестовия метод с производствения код, което означава, че ако промените името на метода в производствения код, трябва да го промените за всеки „тестов метод“, който тества този метод.

Както споменах по-рано, тя е тясно свързана, защото тествате поведението, а не кода, но какво ще стане, ако тествате полезен клас, в този случай можете да използвате тази конвенция, защото е по-проследима, тъй като съдържа име на метод.

Така че за бизнес логиката можете да имате конвенция за именуване, която не съдържа име на метод, например тази конвенция When_StateUnderTest_Expect_ExpectedBehavior. и използвайте друг, който съдържа име на метод за помощни методи, които изискват тестов код, а не поведение.

Примери за конвенция за именуване

Има много конвенции за именуване, като следвате някои от тях:

  • MethodName_StateUnderTest_ExpectedBehavior — пример: getPost_success_postShouldCached
  • MethodName_ExpectedBehavior_StateUnderTest — пример: getPost_postShouldCached_success
  • Should_ExpectedBehavior_When_StateUnderTest — пример: should_throwException_when_NetworkError
  • When_StateUnderTest_Expect_ExpectedBehavior — пример: when_serverError_expect_postNotCached

Няма правилно или грешно при избора на конвенция за именуване, стига да е описателна. Зависи от разработчика и какво предпочита.

По-четлив

Някои съвременни езици, като Kotlin, позволяват писането на тестови методи с интервали.

Затова по-четливо! например, тази конвенция When_StateUnderTest_Expect_ExpectedBehavior ще изглежда по-долу

Избор на тестови случаи

Не можем да тестваме всички възможни случаи, тъй като това би било безкрайно! Вместо това разделяме входа/аргумента на тестваната функция на категории и граници въз основа на нашия случай, след което избираме стойност от всяка категория и граница и ги тестваме.

Няма конкретен брой тестови случаи, зависи от всеки случай. Ако обаче „покритието на кода“ не е 100%, тогава трябва да напишем повече тестове, но 100% не означава, че сме покрили всички възможни случаи. ще стане по-ясно със следващите примери.

Пример #1: Дубликатор на низове

Да предположим, че имаме клас StringDuplicator, който има duplicateString метод, както следва

Когато искаме да дублираме някакъв низ, можем да разделим аргумент/вход (в този случай низ) на три категории празни, един символ и множество символи. първо, нека добавим един тестов случай и да стартираме с покритие

За да стартирате с покритието, можете да изберете „Изпълни StringDuplicatorTest с покритие“, както следва

резултатът ще бъде 100%, тъй като тестовата функция е покрила всички линии

Все още обаче не покриваме всички тестови случаи (три категории, които споменах по-рано), въпреки че StringDuplicator покритието е 100%.

За да покрием всички тестови случаи, можем да добавим още два тестови случая, така че тестовият код ще бъде както следва:

Пример #2: Детектор за конфликтни срещи

Да предположим, че имаме ConflictMeetingsDetector, който има haveMeetingsConflict метод като следния:

Така че тук можем да разделим аргументите (в този случай meetingInterval1 и meetingInterval2) в категории като изображението по-долу

Тестовият код ще бъде както следва:

Покрихме всички категории, също и „изпълнение с покритие“ ще ни даде 100%.
това е страхотно, готови ли сме? все още не. За този единичен тест можем да разделим аргумента на категории и граници.Все още не сме покрили границите.

Лесно забравяме за границите/крайните случаи. много грешки идват от границите. Затова трябва да внимаваме, когато се разделяме на категории и граници.

За да покрием границите, можем да добавим нови два тестови случая като изображението по-долу:

Освен това ще бъдат добавени две нови тестови функции, както следва:

Можете да намерите пълния код в GitHub от тук.

Последна дума

Единичното тестване е също толкова важно, колкото производствения код. Трябва да поддържате тестовия си код възможно най-чист, за да можете да го поддържате, така че модулните тестове ще ви помогнат, в противен случай модулните тестове ще направят кода ви сложен и труден за поддръжка.

Изборът на правилни тестови случаи може да стане чрез разделяне на аргументите/входящите данни в категории и граници.

Също така, по-малко от 100% покритие означава, че трябва да добавим повече тестови случаи, но 100% покритие не означава, че покриваме всеки възможен случай.