Unit testing best
practices

Oleksandr Masalov
Agenda

• зачем нужны юниттесты?
• unit of coverage/work
• требования к юниттестам и как
не нужно писать тесты
• повторное использование
кода(initialization, asserts)
Зачем нужны юниттесты?
поиск багов? (интеграционные тесты)
регрессионное тестирование?
(интеграционные тесты)
улучшение качества кода(дизайна
компонентов)
поиск багов в случае рефакторинга!
(изменение поведения компонента)
источник спецификации системы
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_ExpectedOutput
UnitOfWork_LogicalAction_ExpectedChangeInBehavior
UnitOfWork_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

•
•
•
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

Unit testing best practices

Editor's Notes

  • #22 - Make each test orthogonal (i.e., independent) to all the others - Don’t make unnecessary assertions - Mock out all external services and state - Avoid unnecessary preconditions - Don’t unit-test configuration settings - Name your unit tests clearly and consistently Trust worthy - Make it easy to run - Avoid test logic - Don&apos;t repeat production logic - Dont&apos;t use things that keeps changing Maintanable - Test only publics! Avoid testing private/protected memebers (makes your tests more brittle) - Re-use test code: - Create objects using common methods - Enforce test isolation - No dependency between tests - Don&apos;t run a test from another test - - Test one thing - Avoid multiple asserts on diffrerent objects - Create multiple tests - Hard to name - - one mock per test Readable tests: - project structure - No magic values - Naming a unit tests - Naming variables - separate assert from action - test structure - Use test cases
  • #28 Describes a collection of methods that can create test data objects according to different scenarios