unit testing best practices
DESCRIPTION
.NET IT Share - Unit testing best practicesTRANSCRIPT
Unit testing best practices
Oleksandr Masalov
• зачем нужны юниттесты?• unit of coverage/work• требования к юниттестам и как
не нужно писать тесты• повторное использование
кода(initialization, asserts)
Agenda
Зачем нужны юниттесты?
поиск багов? (интеграционные тесты)регрессионное тестирование?
(интеграционные тесты)улучшение качества кода(дизайна
компонентов)поиск багов в случае рефакторинга!
(изменение поведения компонента)источник спецификации системы
Unit of coverage/work
Return value/exception
Example: Return value/exception
State change
Example: State change
3rd party call
Example: 3rd party call
Требования к юниттестам
Надежные (Trustworthy)
• должны легко запускаться
• failed test == поведение модуля изменилось
• тесты независимы друг от друга
Readable
• Структура
• Именование тестов (self-desciptive)
• Именование переменных (self-desciptive)
• No IFs, SWITCHs (split test)
• 1-15 lines
• Don't Repeat Yourself & Descriptive And Meaningful Phrases
Структура
Именование тест методов
Pattern:
UnitOfWork_Input_ExpectedOutputUnitOfWork_LogicalAction_ExpectedChangeInBehaviorUnitOfWork_ActionOrInput_ExpectedCallToThirdParty
Examples:
Addition_PositiveNumbers_ReturnsSum()Addition_WhenCalled_ResetsTheNextSum()Addition_NegativeNumbers_CallsLogger()
Именование тест методов - 2
Поддерживаемые (Maintainable)
• Тестируйте только public интерфейс
• Не используйте объекты, которые непостоянны(DateTime.Now, Thread, Random etc)
• Не дублируйте продакшин логику
• Не тестируйте всё в одном тесте
• Многократно используйте код для тестов(initialize, assert etc)
Не используйте объекты, которые непостоянны
DateTime.Now, Thread, Random etc
Не дублируйте продакшин логику
Не тестируйте всё в одном тесте
• сложно именовать
• тест не отображает полной картины
Структурируйте тесты!
• Arrange, Act, Assert
• Four-Phase Test
• Given/When/Then
Arrange, Act, Assert - format
if(test.lines <= 3)
{
без форматирования
}else if (test.lines > 3 && !test.hasSubSections)
{
blank line разделитель
}
else{
code comment(Arrange, Act, Assert)
}
Four-Phase Test
Given/When/Then
Code reuse - Initialization problem
Test data initialization:
•Object Mother pattern
•Test Data builder pattern
SUT initialization:
•SUT Mother pattern
•SUT Builder pattern
•Auto-Mocking container
"system under test" = "whatever thing we are testing"(class, object, method)
Object Mother Pattern
var basket = CreateWithDiscount();
var basket = CreateWithSmallDiscount();
var basket = CreateWithLargeDiscount();
var basket = CreateWithLargeDiscountButWithSpecialOffer();
…
Fluent Builder Pattern
var basket = new BasketBuilder().Build();
var basket = new BasketBuilder().WithSmallDiscount().Build();
var basket = new BasketBuilder()
.WithLargeDiscount()
.WithSpecialOffer()
.Build(); …
Example: Test Data Builder
SUT Mother
SUT Builder
Auto-Mocking container pattern
Code reuse - Asserts problem
Object types
• Entities - Identity(long lived Id) Customers Products
• Value Objects - Identity(Value) Money(currency + amount)
• Services - Identity(by default - reference)
Value Objects (Override Equals + GetHashCode)
Test-specific equality
• Comparer concrete IEqualityComparer
• Resemblance(for non-sealed classes)
• Likeness(SemanticComparison)
Comparer
Resemblance (partially comparison)
Order присваевает id продукту
Example: Resemblance
Likeness (convension based comparison)
Q&A