Създавайте приложения за 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% покритие не означава, че покриваме всеки възможен случай.