MS SWIT 2013 Design for Testability

30

description

Презентация к докладу на MS SWIT 2013: Design for Testability: Mocks, Stubs, Refactoring

Transcript of MS SWIT 2013 Design for Testability

Page 1: MS SWIT 2013 Design for Testability
Page 2: MS SWIT 2013 Design for Testability

Design for TestabilityMocks, Stubs, Refactoring

by Sergey Teplyakov, @STeplyakov

Page 3: MS SWIT 2013 Design for Testability

Что не так с дизайном наших

систем?

Page 4: MS SWIT 2013 Design for Testability

Что приводит к плохому дизайну?

• Ошибки на начальных этапах?• Недопонимание требований• Жесткая архитектура• Предварительное обобщение• …

• Постоянные изменения требований?

Page 5: MS SWIT 2013 Design for Testability

Что такое «плохой дизайн»?

Page 6: MS SWIT 2013 Design for Testability

«Главный» критерий плохого дизайна

«А вот я бы сделал это не так!»

That’s not the way I would have done it, TNTWIWHDI

Page 7: MS SWIT 2013 Design for Testability

Критерии плохого дизайна• Жесткость (Rigidity)

• Хрупкость (Fragility)

• Неподвижность (Immobility)

Page 8: MS SWIT 2013 Design for Testability

Юнит-тесты, как лакмусовая бумажка хорошего дизайна

И как этого зверя тестировать?

ServiceLocator

+ Get<T>() : T

<<Interface>>

ILogger

+LogError(error) : void

Configuration

+Instance: Configuration

ClassUnderTest

Database

+GetEmployee(): Employee

<<Interface>>

IServiceProxy

+Compute(Data) : void

<<Interface>>

IViewModelManager

+Show(ViewModel) : void

Page 9: MS SWIT 2013 Design for Testability

«Предусловия» юнит тестов• Требуется ясный «контракт»

класса• Четкий «вход»• Четкий «выход»

• Минимальное количество связей

Page 10: MS SWIT 2013 Design for Testability

Test Doubles: Stubs & Mocks• Стабы - эмулируют состояние• Моки - проверяют поведение

Page 11: MS SWIT 2013 Design for Testability

Пример стаб-объектаLogger

<<Interface>>

ILogConfigurator

+GetConfig() : Config

LogConfiguratorStub

+GetConfig(): Config

Возвращает "поддельный" конфиг

// Добавляем в поддельную конфигурацию из 3-х аппендеровint appenders = 3;var stub = new LogConfiguratorStub(new Config(appenders));var logger = new Logger(stub);

// Проверяем, что логгер сконфигурирован корректноAssert.That(    logger.GetAppenders().Count, Is.EqualTo(appenders));

Page 12: MS SWIT 2013 Design for Testability

Пример мок-объектаLogger

<<Interface>>

ILogAppender

+Write(message) : Void

LogAppenderMock

+Write(message) : Void+WritenMessage: String

Запоминает информацию о

вызовах

    // Arrange    var mock = new LogAppenderMock();    var logger = new Logger(mock);

    // Act    logger.Write("Msg");

    // Assert    Assert.That(mock.WrittenMessage, Is.Not.Null);

Page 13: MS SWIT 2013 Design for Testability

Юнит тесты – не серебряная пуля!

Page 14: MS SWIT 2013 Design for Testability

Наивная реализация модуля расчета заработной платы

Payroll

+ PayEmployees() : void11

CheckWriter

+ WriteCheck() : void

EmployeeDatabase

+ GetEmployee() : Employee+ PutEmployee(e: Employee)

1

1

Employee

+ CalculatePay() : Money+ PostPayment(m: Money)

1 1

«Плохой» дизайн

Page 15: MS SWIT 2013 Design for Testability

Выделяем интерфейсы!

Payroll

+ PayEmployees() : void11

CheckWriter

+ WriteCheck() : void

EmployeeDatabase

+ GetEmployee() : Employee+ PutEmployee(e: Employee)

1

1

Employee

+ CalculatePay() : Money+ PostPayment(m: Money)

1 1<<Interface>>

ICheckWriter

+WriteCheck() : void

<<Interface>>

IEmployeeDatabase

+ GetEmployee() : Employee+ PutEmployee(e: Employee)

<<Interface>>

IEmployee

+ CalculatePay() : Money+ PostPayment(m: Money)

Page 16: MS SWIT 2013 Design for Testability

Теперь дизайн тестируемый!

[Test]public void TestPayroll() {     MockEmployeeDatabase db = new MockEmployeeDatabase();     MockCheckWriter w = new MockCheckWriter();     Payroll p = new Payroll(db, w);     p.PayEmployees();     Assert.IsTrue(w.ChecksWereWrittenCorrectly());     Assert.IsTrue(db.PaymentsWerePostedCorrectly()); }

Page 17: MS SWIT 2013 Design for Testability

Стал ли дизайн лучше?

• Дизайн не изменился!• Груда кода в каждом тесте (*)• Сложность проверки граничных

условий• Динамическая типизация !=

хороший дизайн!

Page 18: MS SWIT 2013 Design for Testability

Альтернативный подход• Нужно ли выделять интерфейс для

CheckWriter-а?• Нужно ли выделять интерфейс для

EmployeeDatabase? • Нет ли скрытых абстракций?• Нужен ли IEmployee?• Не делает ли Employee слишком

много?

Page 19: MS SWIT 2013 Design for Testability

Альтернативный подход

Payroller

+ PayEmployee(Employee)+ CalculatePayment(Money)

1

1

CheckWriter

+ WriteCheck() : void

EmployeeDatabase

+ GetEmployee() : Employee+ PutEmployee(e: Employee)

1

1

Employee

+ PostPayment(m: Money)

1

1

Payroll

+ PayEmployees()

1

1

<<Interface>>

ICheckWriter

+WriteCheck() : void

Переносим логику из Payroll

Убираем метод CalculatePayment

Проверяем интеграционными

тестами

Page 20: MS SWIT 2013 Design for Testability

Идеальный дизайн для тестирования

PaymentCalculator

+Calculate(PaymentInfo) : Money

PaymentInfo

+ WorkScheduler: Scheduler

Money

+ Value: Decimal

Argument Result

Метод Calculate без побочных эффектов!

[TestCaseSource("GetPaymentInfo")]public void Test_Payment_Information(PaymentInfo pi, Money expectedPayment){    // Arrange    var calculator = new PaymentCalculator();

    // Act    var actualPayment = calculator.Calculate(pi);

    // Assert    Assert.That(expectedPayment, Is.EqualTo(actualPayment));}

Page 21: MS SWIT 2013 Design for Testability

А в чем разница?

• Отделение инфраструктуры от логики

• Уменьшение связанности• Возможность повторного

использования• Простота тестов

Page 22: MS SWIT 2013 Design for Testability

Дизайн и борьба со сложностьюЛюбая сложная система строится на основе проверенных модулей более низкого уровня. Гради Буч

Page 23: MS SWIT 2013 Design for Testability

Аксиома управления зависимостямиThe more complex a class or component is, the more decoupled it should be.Ted Faison – Event-Based Programming

Page 24: MS SWIT 2013 Design for Testability

Слепое стремление к тестируемости ведет к …• нарушению инкапсуляции;• проблемам сопровождения;• неявной связности;

Page 25: MS SWIT 2013 Design for Testability

Важное следствие …

Хороший дизайн == тестируемый дизайнТестируемый дизайн != хороший дизайн

AS A RULE OF THUMB

Page 26: MS SWIT 2013 Design for Testability

Design for Testability…

От тестируемости к хорошему дизайну

От хорошего дизайна к тестируемости

Page 27: MS SWIT 2013 Design for Testability

Вопросы?

Page 28: MS SWIT 2013 Design for Testability

Design for Testability: Mocks, Stubs, Refactoring

Sergey Teplyakov

Visual C# MVP

SergeyTeplyakov.blogspot.com

Page 29: MS SWIT 2013 Design for Testability

Заповни АнкетуВиграй Приз

http://anketa.msswit.in.ua

Page 30: MS SWIT 2013 Design for Testability