Unit testing best practices

Post on 16-Jun-2015

274 views 3 download

description

.NET IT Share - Unit testing best practices

Transcript of Unit testing best practices

Unit testing best practices

Oleksandr Masalov

• зачем нужны юниттесты?• unit of coverage/work• требования к юниттестам и как

не нужно писать тесты• повторное использование

кода(initialization, asserts)

Agenda

Зачем нужны юниттесты?

поиск багов? (интеграционные тесты)регрессионное тестирование?

(интеграционные тесты)улучшение качества кода(дизайна

компонентов)поиск багов в случае рефакторинга!

(изменение поведения компонента)источник спецификации системы

masalov.alexander mas
- 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't repeat production logic- Dont't use things that keeps changingMaintanable- 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'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

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

1) DRY - Don't repeat yourself- используйте сетап и тердаун методы для инициализации и удаления данных- проперти в рамках тест класса не используются всеми тест методами(делает тесты тяжелее читаемыми)- дублируемые участки плохи, тем что надо изменять все кусочки, при изменении функционала2) DAMP - made more maintanableDescriptive and meaningful pharses- тесты должны быть легко читаемыми- думай о юниттесте как о дсл-masalov.alexander mas

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