30

Design for Testability: Mocks, Stubs, Refactoring

Embed Size (px)

DESCRIPTION

25 и 26 апреля в Киеве прошла одна из крупнейших специализированных конференций, организованных Microsoft. Главная цель конференции – обсудить представленные программы и продукты, узнать о лучших практиках использования технологий Microsoft от ведущих экспертов международного уровня. Сергей Тепляков, эксперт Luxoft Training по .Net, С++ и архитектуре приложений, представил доклад «Design for Testability: Mocks, Stubs, Refactoring».

Citation preview

Page 1: Design for Testability: Mocks, Stubs, Refactoring
Page 2: Design for Testability: Mocks, Stubs, Refactoring

Design for TestabilityMocks, Stubs, Refactoring

by Sergey Teplyakov, @STeplyakov

Page 3: Design for Testability: Mocks, Stubs, Refactoring

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

систем?

Page 4: Design for Testability: Mocks, Stubs, Refactoring

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

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

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

Page 5: Design for Testability: Mocks, Stubs, Refactoring

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

Page 6: Design for Testability: Mocks, Stubs, Refactoring

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

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

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

Page 7: Design for Testability: Mocks, Stubs, Refactoring

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

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

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

Page 8: Design for Testability: Mocks, Stubs, Refactoring

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

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

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: Design for Testability: Mocks, Stubs, Refactoring

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

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

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

Page 10: Design for Testability: Mocks, Stubs, Refactoring

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

Page 11: Design for Testability: Mocks, Stubs, Refactoring

Пример стаб-объекта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: Design for Testability: Mocks, Stubs, Refactoring

Пример мок-объекта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: Design for Testability: Mocks, Stubs, Refactoring

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

Page 14: Design for Testability: Mocks, Stubs, Refactoring

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

Payroll

+ PayEmployees() : void11

CheckWriter

+ WriteCheck() : void

EmployeeDatabase

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

1

1

Employee

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

1 1

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

Page 15: Design for Testability: Mocks, Stubs, Refactoring

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

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: Design for Testability: Mocks, Stubs, Refactoring

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

[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: Design for Testability: Mocks, Stubs, Refactoring

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

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

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

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

Page 18: Design for Testability: Mocks, Stubs, Refactoring

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

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

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

много?

Page 19: Design for Testability: Mocks, Stubs, Refactoring

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

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: Design for Testability: Mocks, Stubs, Refactoring

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

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: Design for Testability: Mocks, Stubs, Refactoring

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

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

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

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

Page 22: Design for Testability: Mocks, Stubs, Refactoring

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

Page 23: Design for Testability: Mocks, Stubs, Refactoring

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

Page 24: Design for Testability: Mocks, Stubs, Refactoring

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

Page 25: Design for Testability: Mocks, Stubs, Refactoring

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

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

AS A RULE OF THUMB

Page 26: Design for Testability: Mocks, Stubs, Refactoring

Design for Testability…

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

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

Page 27: Design for Testability: Mocks, Stubs, Refactoring

Вопросы?

Page 28: Design for Testability: Mocks, Stubs, Refactoring

Design for Testability: Mocks, Stubs, Refactoring

Sergey Teplyakov

Visual C# MVP

SergeyTeplyakov.blogspot.com

Page 29: Design for Testability: Mocks, Stubs, Refactoring

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

http://anketa.msswit.in.ua

Page 30: Design for Testability: Mocks, Stubs, Refactoring