SlideShare a Scribd company logo
Как всё починить и
ничего не сломать
Работа со сложным кодом при помощи тестов
Все переписать
2
Брыксин Виктор
Senior iOS Developer
iPhone 7
doberman@yandex-team.ru
Контакты:
Спасибо за внимание
Введение
Работа со сложным кодом при помощи тестов
1. Ищем проблемы в коде.
2. Модуль и его зависимости
3. Хорошие и плохие зависимости
4. Как тестировать модуль
5. Практикум
О чем пойдет речь?
5
Проблемы
6
Хрупкость Запутанность Нелокальность
Сложное состояние объектов
Множество запрещенных состояний
Проблемы: хрупкость
7
Множество ответственностей
Тяжело отследить поток управления
Проблемы: запутанность
8
Состояние объектов не
локализовано в самих объектах
Неявное изменение глобального
состояния
Проблемы: нелокальность
9
1. Уменьшают время проверки соответствия требованиям
⌘+U вместо цикла проверки командой QA
2. Фиксируют функциональность
Нет в тестах = не реализовано
Модульные тесты
10
Модуль
11
Содержимое модуля Зависимости
Manager class
Facade class
Storage class
Зависимости
12
Декларация
(Declaration)
Разрешение
(Resolving)
Связь
(Link)
Явная Внешнее Гибкая
Неявная Внутреннее Жесткая
Явная зависимость — зависимость, определенная в
публичном интерфейсе класса.
Неявная зависимость определяется и используется
только в реализации.
Явная и неявная зависимость
13
Внешняя зависимость — зависимость, которая
передается в модуль вызывающим кодом.
Внутренние зависимости создаются или извлекаются
самим модулем.
Внешняя и внутренняя зависимость
14
Гибкая зависимость — зависимость, позволяющая
изменить реализацию без изменения кода модуля.
Жесткие зависимости требуют модификации кода
модуля для изменения поведения.
Гибкая и жесткая зависимость
15
protocol NetworkRequestFactory {
func request() -> NetworkRequest
}
class NetworkManager {
private let _requestFactory: NetworkRequestFactory
public init(withRequestFactory requestFactory: NetworkRequestFactory) {
_requestFactory = requestFactory
}
}
Явная внешняя гибкая зависимость
16
protocol NetworkRequestFactory {
func request() -> NetworkRequest
}
class NetworkRequestFactoryImpl : NetworkRequestFactory {
// ...
}
class NetworkManager {
public var requestFactory: NetworkRequestFactory =
NetworkRequestFactoryImpl()
}
Явная внутренняя гибкая зависимость
17
protocol ServiceLocator {
func resolve<T>() -> T
}
protocol NetworkRequestFactory {
func request() -> NetworkRequest
}
class NetworkManager {
private let _networkRequestFactory: NetworkRequestFactory
public init(withServices serviceLocator: ServiceLocator) {
_networkRequestFactory = serviceLocator.resolve()
}
}
Неявная внешняя гибкая зависимость
Другие варианты: передача в качестве зависимости Any/id 18
protocol NetworkRequestFactory {
func request() -> NetworkRequest
}
class NetworkRequestFactoryImpl : NetworkRequestFactory {
// ...
}
class NetworkManager {
public let requestFactory: NetworkRequestFactory =
NetworkRequestFactoryImpl()
}
Явная внутренняя жесткая зависимость
Другие варианты: наследование 19
class NetworkRequestFactory {
public static var sharedFactory = NetworkRequestFactory()
func request() -> NetworkRequest {
// ...
}
}
class NetworkManager {
private let _requestFactory: NetworkRequestFactory
public init() {
_requestFactory = NetworkRequestFactory.sharedFactory
}
}
Неявная внутренняя жесткая зависимость
Другие варианты: вызовы функций 20
1. Модуль тестируется со всеми жесткими зависимостями
2. Гибкие зависимости заменяются на mock-объекты
3. Проверяется выполнение заявленных контрактов модуля
Тестирование
21
Тестирование
22
Без testing doubles Используя testing doubles
Распутывание клубка
Работа со сложным кодом при помощи тестов
Нужно подготовить код для тестирования.
Выносим плохие зависимости:
1. Singleton,
2. Lazy-loading properties,
3. Создаваемые объекты,
4. GCD,
5. etc…
Вынесение зависимостей
24
class NetworkManager {
public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void {
// Return cached objects if applicable
NetworkFacade.sharedInstance.allArticles {
[weak self] articles in
self?.process(articles: articles)
}
}
private func process(articles: [Article] ) {
// Cache received objects
}
}
Singleton
25
class NetworkManager {
public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void {
NetworkFacade.sharedInstance.allArticles {
[weak self] articles in
self?.process(articles: articles)
}
}
private func process(articles: [Article] ) { … }
}
Singleton
26
class NetworkManager {
public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void {
NetworkFacade.sharedInstance.allArticles {
[weak self] articles in
self?.process(articles: articles)
}
}
private func process(articles: [Article] ) { … }
private let _networkFacade: NetworkFacade
public init(withNetworkFacade networkFacade: NetworkFacade) {
_networkFacade = networkFacade
}
}
Singleton
27
class NetworkManager {
public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void {
NetworkFacade.sharedInstance.allArticles {
[weak self] articles in
self?.process(articles: articles)
}
}
private func process(articles: [Article] ) { … }
private let _networkFacade: NetworkFacade
public init(withNetworkFacade networkFacade: NetworkFacade) { … }
public convenience init() {
self.init(withNetworkFacade: NetworkFacade.sharedInstance)
}
}
Singleton
28
class NetworkManager {
public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void {
_networkFacade.allArticles {
[weak self] articles in
self?.process(articles: articles)
}
}
private func process(articles: [Article] ) { … }
private let _networkFacade: NetworkFacade
public init(withNetworkFacade networkFacade: NetworkFacade) { … }
public convenience init() { … }
}
Singleton
29
class NetworkManager {
private lazy var _networkFacade = NetworkFacade()
public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void {
_networkFacade.allArticles { [weak self] articles in … }
}
}
Lazy-loading properties
30
class NetworkManager {
private lazy var _networkFacade = NetworkFacade()
private let _networkFacadeFactory: () -> NetworkFacade
public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void {
_networkFacade.allArticles { [weak self] articles in … }
}
public init(withNetworkFacadeFactory factory: @escaping () -> NetworkFacade) {
_networkFacadeFactory = factory
}
}
Lazy-loading properties
31
class NetworkManager {
private lazy var _networkFacade = NetworkFacade()
private let _networkFacadeFactory: () -> NetworkFacade
public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void {
_networkFacade.allArticles { [weak self] articles in … }
}
public init(withNetworkFacadeFactory factory: @escaping () -> NetworkFacade) {
_networkFacadeFactory = factory
}
public convenience init() {
self.init(withNetworkFacadeFactory: NetworkFacade.init)
}
}
Lazy-loading properties
32
class NetworkManager {
private lazy var _networkFacade: NetworkFacade = self._networkFacadeFactory()
private let _networkFacadeFactory: () -> NetworkFacade
public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void {
_networkFacade.allArticles { [weak self] articles in }
}
public init(withNetworkFacadeFactory factory: @escaping () -> NetworkFacade) {
_networkFacadeFactory = factory
}
public convenience init() {
self.init(withNetworkFacadeFactory: NetworkFacade.init)
}
}
Lazy-loading properties
33
Для облегчения задачи можно использовать следующие
подходы:
1. Состояние-зависимость
2. Метод-зависимость
Покрытие тестами
34
class ViewController : UIViewController {
private var account: Account
private var articles: [ Article ]
private var drafts: [ Draft ]
init() { }
}
Состояние-зависимость
35
class ViewController : UIViewController {
private var account: Account
private var articles: [ Article ]
private var drafts: [ Draft ]
init() { }
}
class ViewControllerState {
private var account: Account
private var articles: [ Article ]
private var drafts: [ Draft ]
}
Состояние-зависимость
36
class ViewController : UIViewController {
private let _state: ViewControllerState
init(withState state: ViewControllerState) {
_state = state
}
}
class ViewControllerState {
private var account: Account
private var articles: [ Article ]
private var drafts: [ Draft ]
}
Состояние-зависимость
37
class ViewController : UIViewController {
private let _state: ViewControllerState
init(withState state: ViewControllerState) {
_state = state
}
convenience init() {
self.init(withState: ViewControllerState())
}
}
class ViewControllerState {
private var account: Account
private var articles: [ Article ]
private var drafts: [ Draft ]
}
Состояние-зависимость
38
class ViewController : UIViewController {
private func reloadTableView() {
(self.view as! UITableView).reloadData()
}
}
Метод-зависимость
39
class ViewController : UIViewController {
private func reloadTableView() {
ViewController.reloadTableViewImpl(view: self.view)
}
private static func reloadTableViewImpl(view: UIView) -> Void {
(view as! UITableView).reloadData()
}
}
Метод-зависимость
40
class ViewController : UIViewController {
private func reloadTableView() {
self.internalReloadTableView(self.view)
}
private static func reloadTableViewImpl(view: UIView) -> Void { … }
private let internalReloadTableView: (UIView) -> Void =
ViewController.reloadTableViewImpl
}
Метод-зависимость
41
class ViewController : UIViewController {
private func reloadTableView() {
self.internalReloadTableView(self.view)
}
private static func reloadTableViewImpl(view: UIView) -> Void { … }
private let internalReloadTableView: (UIView) -> Void =
ViewController.reloadTableViewImpl
public init(withReloadTableViewImpl reloadTableViewImpl:
@escaping (UIView) -> Void) {
self.internalReloadTableView = reloadTableViewImpl
}
convenience init() {
self.init(withReloadTableViewImpl: ViewController.reloadTableViewImpl)
}
Метод-зависимость
42
git reset --hard HEAD
Все сломали?
43
Покрытие модуля тестами
44
https://www.youtube.com/watch?v=wEhu57pih5w
Miško Hevery — Mr. Testable vs Mr. Untestable (2008)
(top-2 в выдаче по запросу «unit testing»)
Процесс — цепочка преобразований с одним входом и одним
выходом.
Вход и выход процесса — часть внутреннего состояния объекта.
Выделение процессов
45
Процесс
46
class ViewController : UIViewController {
private var _account: Account
private var _articles: [ Articles ]
private var _drafts: [ Drafts ]
}
// Процесс работает только с articles и account
class UpdateArticlesProcess {
public func updateArticles(for account: Account) -> [ Articles ] {
}
}
1. Выделяется процесс в виде внутренней зависимости
2. Процесс покрывается тестами
3. Процесс выносится во внешнюю зависимость
4. Меняются тесты у основного объекта
Итерационное выделение процессов
47
1. Принцип бойскаута
2. Не переделывать все сразу
3. Искать понятные абстракции
Best practices
48
Заключение
Работа со сложным кодом при помощи тестов
1. Тесты помогают вносить изменения
2. Нетестируемый код можно преобразовать в трестируемый
3. С небольшими объектам работать проще, чем с громоздкими
Заключение
50
И даже не пришлось ничего переписывать
51
Брыксин Виктор
Senior iOS Developer
iPhone 7
doberman@yandex-team.ru
Контакты:
Спасибо за внимание

More Related Content

What's hot

Как приручить реактивное программирование
Как приручить реактивное программированиеКак приручить реактивное программирование
Как приручить реактивное программирование
Denis Tsvettsih
 
3. java lecture classes
3. java lecture classes3. java lecture classes
3. java lecture classesMERA_school
 
Как приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложенияхКак приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложениях
Denis Tsvettsih
 
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Denis Tsvettsih
 
Модульная структура
Модульная структураМодульная структура
Модульная структура
Denis Tsvettsih
 
Java Core. Lecture# 3. Part# 3. Multithreading.
Java Core. Lecture# 3. Part# 3. Multithreading.Java Core. Lecture# 3. Part# 3. Multithreading.
Java Core. Lecture# 3. Part# 3. Multithreading.
Anton Moiseenko
 
10. java lecture generics&collections
10. java lecture generics&collections10. java lecture generics&collections
10. java lecture generics&collectionsMERA_school
 
C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 04.C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 04.
Igor Shkulipa
 
Клиент-серверное взаимодействие под android в деталях
Клиент-серверное взаимодействие под android в деталяхКлиент-серверное взаимодействие под android в деталях
Клиент-серверное взаимодействие под android в деталяхKirill Zotin
 
Java Core. Lecture# 5. Concurrency.
Java Core. Lecture# 5. Concurrency.Java Core. Lecture# 5. Concurrency.
Java Core. Lecture# 5. Concurrency.
Anton Moiseenko
 
Лекция Android. БД SQLite, ContentProvider, Loader
Лекция Android. БД SQLite, ContentProvider, LoaderЛекция Android. БД SQLite, ContentProvider, Loader
Лекция Android. БД SQLite, ContentProvider, Loader
Александр Брич
 
Mikhail Valkov_Antipatterns
Mikhail Valkov_AntipatternsMikhail Valkov_Antipatterns
Mikhail Valkov_AntipatternsCiklum
 
Разработка WPF приложений в стиле ViewModel First
Разработка WPF приложений в стиле ViewModel FirstРазработка WPF приложений в стиле ViewModel First
Разработка WPF приложений в стиле ViewModel First
Denis Tsvettsih
 
Java осень 2014 занятие 6
Java осень 2014 занятие 6Java осень 2014 занятие 6
Java осень 2014 занятие 6
Technopark
 

What's hot (14)

Как приручить реактивное программирование
Как приручить реактивное программированиеКак приручить реактивное программирование
Как приручить реактивное программирование
 
3. java lecture classes
3. java lecture classes3. java lecture classes
3. java lecture classes
 
Как приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложенияхКак приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложениях
 
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
 
Модульная структура
Модульная структураМодульная структура
Модульная структура
 
Java Core. Lecture# 3. Part# 3. Multithreading.
Java Core. Lecture# 3. Part# 3. Multithreading.Java Core. Lecture# 3. Part# 3. Multithreading.
Java Core. Lecture# 3. Part# 3. Multithreading.
 
10. java lecture generics&collections
10. java lecture generics&collections10. java lecture generics&collections
10. java lecture generics&collections
 
C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 04.C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 04.
 
Клиент-серверное взаимодействие под android в деталях
Клиент-серверное взаимодействие под android в деталяхКлиент-серверное взаимодействие под android в деталях
Клиент-серверное взаимодействие под android в деталях
 
Java Core. Lecture# 5. Concurrency.
Java Core. Lecture# 5. Concurrency.Java Core. Lecture# 5. Concurrency.
Java Core. Lecture# 5. Concurrency.
 
Лекция Android. БД SQLite, ContentProvider, Loader
Лекция Android. БД SQLite, ContentProvider, LoaderЛекция Android. БД SQLite, ContentProvider, Loader
Лекция Android. БД SQLite, ContentProvider, Loader
 
Mikhail Valkov_Antipatterns
Mikhail Valkov_AntipatternsMikhail Valkov_Antipatterns
Mikhail Valkov_Antipatterns
 
Разработка WPF приложений в стиле ViewModel First
Разработка WPF приложений в стиле ViewModel FirstРазработка WPF приложений в стиле ViewModel First
Разработка WPF приложений в стиле ViewModel First
 
Java осень 2014 занятие 6
Java осень 2014 занятие 6Java осень 2014 занятие 6
Java осень 2014 занятие 6
 

Similar to Виктор Брыкcин — Как всё починить и ничего не сломать: работа со сложным кодом при помощи тестов

Михаил Давыдов "Масштабируемые JavaScript-приложения"
Михаил Давыдов "Масштабируемые JavaScript-приложения"Михаил Давыдов "Масштабируемые JavaScript-приложения"
Михаил Давыдов "Масштабируемые JavaScript-приложения"
Yandex
 
C# Web. Занятие 10.
C# Web. Занятие 10.C# Web. Занятие 10.
C# Web. Занятие 10.
Igor Shkulipa
 
Михаил Давыдов "Масштабируемые JavaScript-приложения"
Михаил Давыдов "Масштабируемые JavaScript-приложения"Михаил Давыдов "Масштабируемые JavaScript-приложения"
Михаил Давыдов "Масштабируемые JavaScript-приложения"
Yandex
 
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNGвебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNGAndrey Rebrov
 
Референсная архитектура приложения на ASP.NET MVC
Референсная архитектура приложения на ASP.NET MVCРеференсная архитектура приложения на ASP.NET MVC
Референсная архитектура приложения на ASP.NET MVCAndrew Mayorov
 
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Andrey Rebrov
 
"Погружение в Robolectric" Дмитрий Костырев (Avito)
"Погружение в Robolectric"  Дмитрий Костырев (Avito)"Погружение в Robolectric"  Дмитрий Костырев (Avito)
"Погружение в Robolectric" Дмитрий Костырев (Avito)
AvitoTech
 
Droidcon Moscow 2015. Dagger2 практический ликбез по работе с кинжалами. Дмит...
Droidcon Moscow 2015. Dagger2 практический ликбез по работе с кинжалами. Дмит...Droidcon Moscow 2015. Dagger2 практический ликбез по работе с кинжалами. Дмит...
Droidcon Moscow 2015. Dagger2 практический ликбез по работе с кинжалами. Дмит...
Mail.ru Group
 
Руслан Ханов, «Контейнер сервисов — Что? Где? Когда?»
Руслан Ханов, «Контейнер сервисов — Что? Где? Когда?»Руслан Ханов, «Контейнер сервисов — Что? Где? Когда?»
Руслан Ханов, «Контейнер сервисов — Что? Где? Когда?»
Mail.ru Group
 
Mobile automation uamobile
Mobile automation uamobileMobile automation uamobile
Mobile automation uamobileUA Mobile
 
Контейнер сервисов
Контейнер сервисовКонтейнер сервисов
Контейнер сервисов
Ruslan Hanov
 
Deep Dive in Magento DI
Deep Dive in Magento DIDeep Dive in Magento DI
Deep Dive in Magento DI
Magecom UK Limited
 
iOS and Android Mobile Test Automation
iOS and Android Mobile Test AutomationiOS and Android Mobile Test Automation
iOS and Android Mobile Test Automation
Andrii Dzynia
 
Dependency injection на примере unity и n inject
Dependency injection на примере unity и n injectDependency injection на примере unity и n inject
Dependency injection на примере unity и n injectRoman Kalita
 
Java 9: what is there beyond modularization
Java 9: what is there beyond modularizationJava 9: what is there beyond modularization
Java 9: what is there beyond modularization
Ivan Krylov
 
Введение в Android-разработку (Lecture 06 – basics)
Введение в Android-разработку (Lecture 06 – basics)Введение в Android-разработку (Lecture 06 – basics)
Введение в Android-разработку (Lecture 06 – basics)
Noveo
 
Web осень 2013 лекция 8
Web осень 2013 лекция 8Web осень 2013 лекция 8
Web осень 2013 лекция 8Technopark
 
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Sigma Software
 
Mobile Fest#spb 2012
Mobile Fest#spb 2012Mobile Fest#spb 2012
Mobile Fest#spb 2012
dmalykhanov
 
C# Desktop. Занятие 07.
C# Desktop. Занятие 07.C# Desktop. Занятие 07.
C# Desktop. Занятие 07.
Igor Shkulipa
 

Similar to Виктор Брыкcин — Как всё починить и ничего не сломать: работа со сложным кодом при помощи тестов (20)

Михаил Давыдов "Масштабируемые JavaScript-приложения"
Михаил Давыдов "Масштабируемые JavaScript-приложения"Михаил Давыдов "Масштабируемые JavaScript-приложения"
Михаил Давыдов "Масштабируемые JavaScript-приложения"
 
C# Web. Занятие 10.
C# Web. Занятие 10.C# Web. Занятие 10.
C# Web. Занятие 10.
 
Михаил Давыдов "Масштабируемые JavaScript-приложения"
Михаил Давыдов "Масштабируемые JavaScript-приложения"Михаил Давыдов "Масштабируемые JavaScript-приложения"
Михаил Давыдов "Масштабируемые JavaScript-приложения"
 
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNGвебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
 
Референсная архитектура приложения на ASP.NET MVC
Референсная архитектура приложения на ASP.NET MVCРеференсная архитектура приложения на ASP.NET MVC
Референсная архитектура приложения на ASP.NET MVC
 
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
 
"Погружение в Robolectric" Дмитрий Костырев (Avito)
"Погружение в Robolectric"  Дмитрий Костырев (Avito)"Погружение в Robolectric"  Дмитрий Костырев (Avito)
"Погружение в Robolectric" Дмитрий Костырев (Avito)
 
Droidcon Moscow 2015. Dagger2 практический ликбез по работе с кинжалами. Дмит...
Droidcon Moscow 2015. Dagger2 практический ликбез по работе с кинжалами. Дмит...Droidcon Moscow 2015. Dagger2 практический ликбез по работе с кинжалами. Дмит...
Droidcon Moscow 2015. Dagger2 практический ликбез по работе с кинжалами. Дмит...
 
Руслан Ханов, «Контейнер сервисов — Что? Где? Когда?»
Руслан Ханов, «Контейнер сервисов — Что? Где? Когда?»Руслан Ханов, «Контейнер сервисов — Что? Где? Когда?»
Руслан Ханов, «Контейнер сервисов — Что? Где? Когда?»
 
Mobile automation uamobile
Mobile automation uamobileMobile automation uamobile
Mobile automation uamobile
 
Контейнер сервисов
Контейнер сервисовКонтейнер сервисов
Контейнер сервисов
 
Deep Dive in Magento DI
Deep Dive in Magento DIDeep Dive in Magento DI
Deep Dive in Magento DI
 
iOS and Android Mobile Test Automation
iOS and Android Mobile Test AutomationiOS and Android Mobile Test Automation
iOS and Android Mobile Test Automation
 
Dependency injection на примере unity и n inject
Dependency injection на примере unity и n injectDependency injection на примере unity и n inject
Dependency injection на примере unity и n inject
 
Java 9: what is there beyond modularization
Java 9: what is there beyond modularizationJava 9: what is there beyond modularization
Java 9: what is there beyond modularization
 
Введение в Android-разработку (Lecture 06 – basics)
Введение в Android-разработку (Lecture 06 – basics)Введение в Android-разработку (Lecture 06 – basics)
Введение в Android-разработку (Lecture 06 – basics)
 
Web осень 2013 лекция 8
Web осень 2013 лекция 8Web осень 2013 лекция 8
Web осень 2013 лекция 8
 
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
 
Mobile Fest#spb 2012
Mobile Fest#spb 2012Mobile Fest#spb 2012
Mobile Fest#spb 2012
 
C# Desktop. Занятие 07.
C# Desktop. Занятие 07.C# Desktop. Занятие 07.
C# Desktop. Занятие 07.
 

More from CocoaHeads

Дмитрий Котенко – Реактивный VIPER
Дмитрий Котенко – Реактивный VIPERДмитрий Котенко – Реактивный VIPER
Дмитрий Котенко – Реактивный VIPER
CocoaHeads
 
Александр Зимин – Анимация как средство самовыражения
Александр Зимин – Анимация как средство самовыраженияАлександр Зимин – Анимация как средство самовыражения
Александр Зимин – Анимация как средство самовыражения
CocoaHeads
 
Николай Ашанин – Team Lead. Структурирование мыслей
Николай Ашанин – Team Lead. Структурирование мыслейНиколай Ашанин – Team Lead. Структурирование мыслей
Николай Ашанин – Team Lead. Структурирование мыслей
CocoaHeads
 
Кирилл Аверьянов — Кастомная кнопка: взгляд изнутри
Кирилл Аверьянов —  Кастомная кнопка: взгляд изнутриКирилл Аверьянов —  Кастомная кнопка: взгляд изнутри
Кирилл Аверьянов — Кастомная кнопка: взгляд изнутри
CocoaHeads
 
Самвел Меджлумян — S3: API на Swift за пять минут
Самвел Меджлумян —  S3: API на Swift за пять минутСамвел Меджлумян —  S3: API на Swift за пять минут
Самвел Меджлумян — S3: API на Swift за пять минут
CocoaHeads
 
Александр Зимин (Alexander Zimin) — Магия Swift
Александр Зимин (Alexander Zimin) — Магия SwiftАлександр Зимин (Alexander Zimin) — Магия Swift
Александр Зимин (Alexander Zimin) — Магия Swift
CocoaHeads
 
Катерина Трофименко — Разработка фич: от флагов до a/b-тестов
Катерина Трофименко — Разработка фич: от флагов до a/b-тестовКатерина Трофименко — Разработка фич: от флагов до a/b-тестов
Катерина Трофименко — Разработка фич: от флагов до a/b-тестов
CocoaHeads
 
Андрей Володин — Как подружиться с роботом
Андрей Володин — Как подружиться с роботомАндрей Володин — Как подружиться с роботом
Андрей Володин — Как подружиться с роботом
CocoaHeads
 
Александр Зимин — Мобильные интерфейсы будущего
Александр Зимин — Мобильные интерфейсы будущегоАлександр Зимин — Мобильные интерфейсы будущего
Александр Зимин — Мобильные интерфейсы будущего
CocoaHeads
 
Николай Волосатов — Работа с крэшами библиотек
Николай Волосатов — Работа с крэшами библиотекНиколай Волосатов — Работа с крэшами библиотек
Николай Волосатов — Работа с крэшами библиотек
CocoaHeads
 
Вадим Дробинин (Vadim Drobinin) — Заботимся правильно: CareKit, HealthKit и ...
Вадим Дробинин (Vadim Drobinin) —  Заботимся правильно: CareKit, HealthKit и ...Вадим Дробинин (Vadim Drobinin) —  Заботимся правильно: CareKit, HealthKit и ...
Вадим Дробинин (Vadim Drobinin) — Заботимся правильно: CareKit, HealthKit и ...
CocoaHeads
 
Александр Зимин (Alexander Zimin) — UIViewController, откройся!
Александр Зимин (Alexander Zimin) — UIViewController, откройся!Александр Зимин (Alexander Zimin) — UIViewController, откройся!
Александр Зимин (Alexander Zimin) — UIViewController, откройся!
CocoaHeads
 
Сергей Пронин, Никита Кошолкин — Как мы разрабатываем App in the Air: процесс...
Сергей Пронин, Никита Кошолкин — Как мы разрабатываем App in the Air: процесс...Сергей Пронин, Никита Кошолкин — Как мы разрабатываем App in the Air: процесс...
Сергей Пронин, Никита Кошолкин — Как мы разрабатываем App in the Air: процесс...
CocoaHeads
 
Макс Грибов — Использование SpriteKit в неигровых приложениях
Макс Грибов — Использование SpriteKit в неигровых приложенияхМакс Грибов — Использование SpriteKit в неигровых приложениях
Макс Грибов — Использование SpriteKit в неигровых приложениях
CocoaHeads
 
Михаил Рахманов — Promises, или почему обещания надо выполнять
Михаил Рахманов — Promises, или почему обещания надо выполнятьМихаил Рахманов — Promises, или почему обещания надо выполнять
Михаил Рахманов — Promises, или почему обещания надо выполнять
CocoaHeads
 
Александр Зимин — Оптимизация разработки
Александр Зимин — Оптимизация разработкиАлександр Зимин — Оптимизация разработки
Александр Зимин — Оптимизация разработки
CocoaHeads
 
Алина Михайлова — Как обойтись без менеджера в своем проекте
Алина Михайлова — Как обойтись без менеджера в своем проектеАлина Михайлова — Как обойтись без менеджера в своем проекте
Алина Михайлова — Как обойтись без менеджера в своем проекте
CocoaHeads
 

More from CocoaHeads (17)

Дмитрий Котенко – Реактивный VIPER
Дмитрий Котенко – Реактивный VIPERДмитрий Котенко – Реактивный VIPER
Дмитрий Котенко – Реактивный VIPER
 
Александр Зимин – Анимация как средство самовыражения
Александр Зимин – Анимация как средство самовыраженияАлександр Зимин – Анимация как средство самовыражения
Александр Зимин – Анимация как средство самовыражения
 
Николай Ашанин – Team Lead. Структурирование мыслей
Николай Ашанин – Team Lead. Структурирование мыслейНиколай Ашанин – Team Lead. Структурирование мыслей
Николай Ашанин – Team Lead. Структурирование мыслей
 
Кирилл Аверьянов — Кастомная кнопка: взгляд изнутри
Кирилл Аверьянов —  Кастомная кнопка: взгляд изнутриКирилл Аверьянов —  Кастомная кнопка: взгляд изнутри
Кирилл Аверьянов — Кастомная кнопка: взгляд изнутри
 
Самвел Меджлумян — S3: API на Swift за пять минут
Самвел Меджлумян —  S3: API на Swift за пять минутСамвел Меджлумян —  S3: API на Swift за пять минут
Самвел Меджлумян — S3: API на Swift за пять минут
 
Александр Зимин (Alexander Zimin) — Магия Swift
Александр Зимин (Alexander Zimin) — Магия SwiftАлександр Зимин (Alexander Zimin) — Магия Swift
Александр Зимин (Alexander Zimin) — Магия Swift
 
Катерина Трофименко — Разработка фич: от флагов до a/b-тестов
Катерина Трофименко — Разработка фич: от флагов до a/b-тестовКатерина Трофименко — Разработка фич: от флагов до a/b-тестов
Катерина Трофименко — Разработка фич: от флагов до a/b-тестов
 
Андрей Володин — Как подружиться с роботом
Андрей Володин — Как подружиться с роботомАндрей Володин — Как подружиться с роботом
Андрей Володин — Как подружиться с роботом
 
Александр Зимин — Мобильные интерфейсы будущего
Александр Зимин — Мобильные интерфейсы будущегоАлександр Зимин — Мобильные интерфейсы будущего
Александр Зимин — Мобильные интерфейсы будущего
 
Николай Волосатов — Работа с крэшами библиотек
Николай Волосатов — Работа с крэшами библиотекНиколай Волосатов — Работа с крэшами библиотек
Николай Волосатов — Работа с крэшами библиотек
 
Вадим Дробинин (Vadim Drobinin) — Заботимся правильно: CareKit, HealthKit и ...
Вадим Дробинин (Vadim Drobinin) —  Заботимся правильно: CareKit, HealthKit и ...Вадим Дробинин (Vadim Drobinin) —  Заботимся правильно: CareKit, HealthKit и ...
Вадим Дробинин (Vadim Drobinin) — Заботимся правильно: CareKit, HealthKit и ...
 
Александр Зимин (Alexander Zimin) — UIViewController, откройся!
Александр Зимин (Alexander Zimin) — UIViewController, откройся!Александр Зимин (Alexander Zimin) — UIViewController, откройся!
Александр Зимин (Alexander Zimin) — UIViewController, откройся!
 
Сергей Пронин, Никита Кошолкин — Как мы разрабатываем App in the Air: процесс...
Сергей Пронин, Никита Кошолкин — Как мы разрабатываем App in the Air: процесс...Сергей Пронин, Никита Кошолкин — Как мы разрабатываем App in the Air: процесс...
Сергей Пронин, Никита Кошолкин — Как мы разрабатываем App in the Air: процесс...
 
Макс Грибов — Использование SpriteKit в неигровых приложениях
Макс Грибов — Использование SpriteKit в неигровых приложенияхМакс Грибов — Использование SpriteKit в неигровых приложениях
Макс Грибов — Использование SpriteKit в неигровых приложениях
 
Михаил Рахманов — Promises, или почему обещания надо выполнять
Михаил Рахманов — Promises, или почему обещания надо выполнятьМихаил Рахманов — Promises, или почему обещания надо выполнять
Михаил Рахманов — Promises, или почему обещания надо выполнять
 
Александр Зимин — Оптимизация разработки
Александр Зимин — Оптимизация разработкиАлександр Зимин — Оптимизация разработки
Александр Зимин — Оптимизация разработки
 
Алина Михайлова — Как обойтись без менеджера в своем проекте
Алина Михайлова — Как обойтись без менеджера в своем проектеАлина Михайлова — Как обойтись без менеджера в своем проекте
Алина Михайлова — Как обойтись без менеджера в своем проекте
 

Виктор Брыкcин — Как всё починить и ничего не сломать: работа со сложным кодом при помощи тестов

  • 1. Как всё починить и ничего не сломать Работа со сложным кодом при помощи тестов
  • 3. Брыксин Виктор Senior iOS Developer iPhone 7 doberman@yandex-team.ru Контакты: Спасибо за внимание
  • 4. Введение Работа со сложным кодом при помощи тестов
  • 5. 1. Ищем проблемы в коде. 2. Модуль и его зависимости 3. Хорошие и плохие зависимости 4. Как тестировать модуль 5. Практикум О чем пойдет речь? 5
  • 7. Сложное состояние объектов Множество запрещенных состояний Проблемы: хрупкость 7
  • 8. Множество ответственностей Тяжело отследить поток управления Проблемы: запутанность 8
  • 9. Состояние объектов не локализовано в самих объектах Неявное изменение глобального состояния Проблемы: нелокальность 9
  • 10. 1. Уменьшают время проверки соответствия требованиям ⌘+U вместо цикла проверки командой QA 2. Фиксируют функциональность Нет в тестах = не реализовано Модульные тесты 10
  • 13. Явная зависимость — зависимость, определенная в публичном интерфейсе класса. Неявная зависимость определяется и используется только в реализации. Явная и неявная зависимость 13
  • 14. Внешняя зависимость — зависимость, которая передается в модуль вызывающим кодом. Внутренние зависимости создаются или извлекаются самим модулем. Внешняя и внутренняя зависимость 14
  • 15. Гибкая зависимость — зависимость, позволяющая изменить реализацию без изменения кода модуля. Жесткие зависимости требуют модификации кода модуля для изменения поведения. Гибкая и жесткая зависимость 15
  • 16. protocol NetworkRequestFactory { func request() -> NetworkRequest } class NetworkManager { private let _requestFactory: NetworkRequestFactory public init(withRequestFactory requestFactory: NetworkRequestFactory) { _requestFactory = requestFactory } } Явная внешняя гибкая зависимость 16
  • 17. protocol NetworkRequestFactory { func request() -> NetworkRequest } class NetworkRequestFactoryImpl : NetworkRequestFactory { // ... } class NetworkManager { public var requestFactory: NetworkRequestFactory = NetworkRequestFactoryImpl() } Явная внутренняя гибкая зависимость 17
  • 18. protocol ServiceLocator { func resolve<T>() -> T } protocol NetworkRequestFactory { func request() -> NetworkRequest } class NetworkManager { private let _networkRequestFactory: NetworkRequestFactory public init(withServices serviceLocator: ServiceLocator) { _networkRequestFactory = serviceLocator.resolve() } } Неявная внешняя гибкая зависимость Другие варианты: передача в качестве зависимости Any/id 18
  • 19. protocol NetworkRequestFactory { func request() -> NetworkRequest } class NetworkRequestFactoryImpl : NetworkRequestFactory { // ... } class NetworkManager { public let requestFactory: NetworkRequestFactory = NetworkRequestFactoryImpl() } Явная внутренняя жесткая зависимость Другие варианты: наследование 19
  • 20. class NetworkRequestFactory { public static var sharedFactory = NetworkRequestFactory() func request() -> NetworkRequest { // ... } } class NetworkManager { private let _requestFactory: NetworkRequestFactory public init() { _requestFactory = NetworkRequestFactory.sharedFactory } } Неявная внутренняя жесткая зависимость Другие варианты: вызовы функций 20
  • 21. 1. Модуль тестируется со всеми жесткими зависимостями 2. Гибкие зависимости заменяются на mock-объекты 3. Проверяется выполнение заявленных контрактов модуля Тестирование 21
  • 22. Тестирование 22 Без testing doubles Используя testing doubles
  • 23. Распутывание клубка Работа со сложным кодом при помощи тестов
  • 24. Нужно подготовить код для тестирования. Выносим плохие зависимости: 1. Singleton, 2. Lazy-loading properties, 3. Создаваемые объекты, 4. GCD, 5. etc… Вынесение зависимостей 24
  • 25. class NetworkManager { public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void { // Return cached objects if applicable NetworkFacade.sharedInstance.allArticles { [weak self] articles in self?.process(articles: articles) } } private func process(articles: [Article] ) { // Cache received objects } } Singleton 25
  • 26. class NetworkManager { public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void { NetworkFacade.sharedInstance.allArticles { [weak self] articles in self?.process(articles: articles) } } private func process(articles: [Article] ) { … } } Singleton 26
  • 27. class NetworkManager { public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void { NetworkFacade.sharedInstance.allArticles { [weak self] articles in self?.process(articles: articles) } } private func process(articles: [Article] ) { … } private let _networkFacade: NetworkFacade public init(withNetworkFacade networkFacade: NetworkFacade) { _networkFacade = networkFacade } } Singleton 27
  • 28. class NetworkManager { public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void { NetworkFacade.sharedInstance.allArticles { [weak self] articles in self?.process(articles: articles) } } private func process(articles: [Article] ) { … } private let _networkFacade: NetworkFacade public init(withNetworkFacade networkFacade: NetworkFacade) { … } public convenience init() { self.init(withNetworkFacade: NetworkFacade.sharedInstance) } } Singleton 28
  • 29. class NetworkManager { public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void { _networkFacade.allArticles { [weak self] articles in self?.process(articles: articles) } } private func process(articles: [Article] ) { … } private let _networkFacade: NetworkFacade public init(withNetworkFacade networkFacade: NetworkFacade) { … } public convenience init() { … } } Singleton 29
  • 30. class NetworkManager { private lazy var _networkFacade = NetworkFacade() public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void { _networkFacade.allArticles { [weak self] articles in … } } } Lazy-loading properties 30
  • 31. class NetworkManager { private lazy var _networkFacade = NetworkFacade() private let _networkFacadeFactory: () -> NetworkFacade public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void { _networkFacade.allArticles { [weak self] articles in … } } public init(withNetworkFacadeFactory factory: @escaping () -> NetworkFacade) { _networkFacadeFactory = factory } } Lazy-loading properties 31
  • 32. class NetworkManager { private lazy var _networkFacade = NetworkFacade() private let _networkFacadeFactory: () -> NetworkFacade public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void { _networkFacade.allArticles { [weak self] articles in … } } public init(withNetworkFacadeFactory factory: @escaping () -> NetworkFacade) { _networkFacadeFactory = factory } public convenience init() { self.init(withNetworkFacadeFactory: NetworkFacade.init) } } Lazy-loading properties 32
  • 33. class NetworkManager { private lazy var _networkFacade: NetworkFacade = self._networkFacadeFactory() private let _networkFacadeFactory: () -> NetworkFacade public func allArticles(_ callback: @escaping ([ Article ]) -> Void) -> Void { _networkFacade.allArticles { [weak self] articles in } } public init(withNetworkFacadeFactory factory: @escaping () -> NetworkFacade) { _networkFacadeFactory = factory } public convenience init() { self.init(withNetworkFacadeFactory: NetworkFacade.init) } } Lazy-loading properties 33
  • 34. Для облегчения задачи можно использовать следующие подходы: 1. Состояние-зависимость 2. Метод-зависимость Покрытие тестами 34
  • 35. class ViewController : UIViewController { private var account: Account private var articles: [ Article ] private var drafts: [ Draft ] init() { } } Состояние-зависимость 35
  • 36. class ViewController : UIViewController { private var account: Account private var articles: [ Article ] private var drafts: [ Draft ] init() { } } class ViewControllerState { private var account: Account private var articles: [ Article ] private var drafts: [ Draft ] } Состояние-зависимость 36
  • 37. class ViewController : UIViewController { private let _state: ViewControllerState init(withState state: ViewControllerState) { _state = state } } class ViewControllerState { private var account: Account private var articles: [ Article ] private var drafts: [ Draft ] } Состояние-зависимость 37
  • 38. class ViewController : UIViewController { private let _state: ViewControllerState init(withState state: ViewControllerState) { _state = state } convenience init() { self.init(withState: ViewControllerState()) } } class ViewControllerState { private var account: Account private var articles: [ Article ] private var drafts: [ Draft ] } Состояние-зависимость 38
  • 39. class ViewController : UIViewController { private func reloadTableView() { (self.view as! UITableView).reloadData() } } Метод-зависимость 39
  • 40. class ViewController : UIViewController { private func reloadTableView() { ViewController.reloadTableViewImpl(view: self.view) } private static func reloadTableViewImpl(view: UIView) -> Void { (view as! UITableView).reloadData() } } Метод-зависимость 40
  • 41. class ViewController : UIViewController { private func reloadTableView() { self.internalReloadTableView(self.view) } private static func reloadTableViewImpl(view: UIView) -> Void { … } private let internalReloadTableView: (UIView) -> Void = ViewController.reloadTableViewImpl } Метод-зависимость 41
  • 42. class ViewController : UIViewController { private func reloadTableView() { self.internalReloadTableView(self.view) } private static func reloadTableViewImpl(view: UIView) -> Void { … } private let internalReloadTableView: (UIView) -> Void = ViewController.reloadTableViewImpl public init(withReloadTableViewImpl reloadTableViewImpl: @escaping (UIView) -> Void) { self.internalReloadTableView = reloadTableViewImpl } convenience init() { self.init(withReloadTableViewImpl: ViewController.reloadTableViewImpl) } Метод-зависимость 42
  • 43. git reset --hard HEAD Все сломали? 43
  • 44. Покрытие модуля тестами 44 https://www.youtube.com/watch?v=wEhu57pih5w Miško Hevery — Mr. Testable vs Mr. Untestable (2008) (top-2 в выдаче по запросу «unit testing»)
  • 45. Процесс — цепочка преобразований с одним входом и одним выходом. Вход и выход процесса — часть внутреннего состояния объекта. Выделение процессов 45
  • 46. Процесс 46 class ViewController : UIViewController { private var _account: Account private var _articles: [ Articles ] private var _drafts: [ Drafts ] } // Процесс работает только с articles и account class UpdateArticlesProcess { public func updateArticles(for account: Account) -> [ Articles ] { } }
  • 47. 1. Выделяется процесс в виде внутренней зависимости 2. Процесс покрывается тестами 3. Процесс выносится во внешнюю зависимость 4. Меняются тесты у основного объекта Итерационное выделение процессов 47
  • 48. 1. Принцип бойскаута 2. Не переделывать все сразу 3. Искать понятные абстракции Best practices 48
  • 49. Заключение Работа со сложным кодом при помощи тестов
  • 50. 1. Тесты помогают вносить изменения 2. Нетестируемый код можно преобразовать в трестируемый 3. С небольшими объектам работать проще, чем с громоздкими Заключение 50
  • 51. И даже не пришлось ничего переписывать 51
  • 52. Брыксин Виктор Senior iOS Developer iPhone 7 doberman@yandex-team.ru Контакты: Спасибо за внимание