SlideShare a Scribd company logo
1 of 41
Download to read offline
Refactor code to testable
Hokila

Cocoa heads Taipei 2017.11
2
什什麼是 Unit Test
ModuleInput Output
3
現實上的難關
常⾒見見說法
feature 都來來不及做了了,哪有時間寫
我們家有 QA
有 UI Test 啊
4
現實上的難關
實際上
不會寫
Input 無法控制 (API , 不是⾃自⼰己寫的 SDK)
ViewController 超級無敵⼤大
5
Benefit
• 同樣的 code ⽤用兩兩種思考⽅方式看過
• ⾃自然⽽而然學會切 module,或者合併商業邏輯
• ⼀一次跑完幾百個幾千的 check,令⼈人安⼼心
• ⽽而且很快
實際案例例
login View Controller
7
LoginViewController
login API
AccountKit login API
Facebook login API
save Token after login success
tracking event
set User Info
8
問題 - dependency 太多
login API,成功 or 失敗
facebook / AccountKit SDK ⾏行行為
是否有存下 AccessToken
User 資料是不是該有的都有 (UserID , MemberShip)
該有的 tracking 是不是有送
login 時
login 後
拆出 login manager
拆出 login manager
不知道在幹嘛的 module 叫 manager 就對了了
10
protocol LoginManagerDelegate:class{
func loginStart()
func loginSuccess(status:LoginStatus)
func loginFail(json:[String:Any]?,error:NSError?)
}
class LoginManager{
static let shareInstance = LoginManager()
weak var delegate:LoginManagerDelegate?
func loginFB()
func loginAccountKit()
}
11
class LoginController: BaseViewController{
func touchFBlogin(_ sender: AnyObject) {
self.loginManager.loginFB()
}
func touchAClogin(_ sender: AnyObject){
self.loginManager.loginAccountKit()
}
}
extension LoginController:LoginManagerDelegate{
func loginStart(){ //show indicator }
func loginFail(json:[String:Any]?,error:NSError?){ //error handling}
func loginSuccess(status:LoginStatus){ //success flow }
}
12
LoginViewController LoginManager
login API
AccountKit login API
Facebook login API
save Token after login success
tracking event
set User Info
login start
login fail handler
login success handler
12
LoginViewController LoginManager
login API
AccountKit login API
Facebook login API
save Token after login success
tracking event
set User Info
login start
login fail handler
login success handler
12
LoginViewController LoginManager
login API
AccountKit login API
Facebook login API
save Token after login success
tracking event
set User Info
login start
login fail handler
login success handler
13
Login
Manager
login API
AccountKit login API
Facebook login API
LoginManagerDelegate
save Token
tracking event
set User Info
dependency injection
15
模擬 login API ⾏行行為
protocol LoginAPIDelegate {
func loginToken(token:String?,callback:@escaping loginAPICompletion)
}
extension API:LoginAPIDelegate{
func loginToken(token:String?,callback:@escaping loginAPICompletion{
// call real API here
}
}
16
模擬 Facebook SDK response ⾏行行為
protocol FBLoginManagerDelegate {
var currentAccessToken:String? { get }
……
}
class FBLogInManager:FBLoginManagerDelegate {
var currentAccessToken:String? {
return FBSDKAccessToken.current()?.tokenString
}
……
}
17
class LoginManager{
static let shareInstance = LoginManager()
weak var delegate:LoginManagerDelegate?
private var loginAPI:LoginAPIDelegate
private let accountKit:AccountKitDelegate
private let fb:FBLoginManagerDelegate
init(api:LoginAPIDelegate = API.shareInstance,
ac:AccountKitDelegate = AKFAccountKit(responseType: .accessToken),
fblogin:FBLoginManagerDelegate = FBLogInManager()) {
self.loginAPI = api
self.accountKit = ac
self.fb = fblogin
}
}
Real Instance
Prepare stub module
19
class MockLoginAPI:LoginAPIDelegate {
var loginCallback:(LoginStatus,[String:Any]?, AccessToken?, NSError?)
func loginToken(token:String?,callback:@escaping loginAPICompletion {
callback(loginCallback.0,loginCallback.1,
loginCallback.2,loginCallback.3)
}
}
class MockFBLogin:FBLoginManagerDelegate {
var currentAccessToken: String?
}
20
func testFBloginSuccess() {
//1.prepare input
mockFBLogin.currentAccessToken = "123456789"
let loginJSON = loadData(fileName: "loginSuccess", type: "json")
mockLoginAPI.loginCallback = (.loginSuccess,loginJSON,authToken,nil)
//2.Compose Login Manager
self.loginManager = KTLoginManager(api: mockLoginAPI,
ac: mockAC,
fblogin: mockFBLogin)
self.loginManager.delegate = mockLoginVC
//3.trigger login
loginManager.loginFB()
//4.Verify output
……..
}
21
func testFBloginSuccess() {
……..
//4.Verify output
XCTAssertTrue(mockLoginVC.isLoginSuccess)
XCTAssertNotNil(mockAccessTokenManager.lastAccessToken)
XCTAssertEqual(KTUser.shareUser.info.memberShip, .premium)
XCTAssertNotNil(KTUser.shareUser.info.userID)
XCTAssertEqual(mockTracker.lastStatus, .loginSuccess)
XCTAssertEqual(mockTracker.lastLoginType, .facebook)
XCTAssertNotNil(mockTracker.lastLoginUserInfo.userID)
}
dependency injection 問題
inject 的 module 太多,不確定有沒有漏
23
class LoginManager{
static let shareInstance = LoginManager()
weak var delegate:LoginManagerDelegate?
private var loginAPI:LoginAPIDelegate
private let accountKit:AccountKitDelegate
private let fb:FBLoginManagerDelegate
init(api:LoginAPIDelegate = API.shareInstance,
ac:AccountKitDelegate = AKFAccountKit(responseType: .accessToken),
fblogin:FBLoginManagerDelegate = FBLogInManager()) {
self.loginAPI = api
self.accountKit = ac
self.fb = fblogin
}
}
Real Instance
24
struct LoginManagerInject{
var loginAPI:KTLoginAPIDelegate = KTAPI.shareInstance
var accountKit:KTAccountKitDelegate = AKFAccountKit(responseType: .accessTok
var fb:KTFBLoginManagerDelegate = FBLogInManager()
}
class LoginManager{
static let shareInstance = LoginManager()
weak var delegate:LoginManagerDelegate?
private let inject:LoginManagerInject
init(inject:LoginManagerInject = LoginManagerInject() ){
self.inject = inject
}
}
24
struct LoginManagerInject{
var loginAPI:KTLoginAPIDelegate = KTAPI.shareInstance
var accountKit:KTAccountKitDelegate = AKFAccountKit(responseType: .accessTok
var fb:KTFBLoginManagerDelegate = FBLogInManager()
}
class LoginManager{
static let shareInstance = LoginManager()
weak var delegate:LoginManagerDelegate?
private let inject:LoginManagerInject
init(inject:LoginManagerInject = LoginManagerInject() ){
self.inject = inject
}
}
self.loginAPI
24
struct LoginManagerInject{
var loginAPI:KTLoginAPIDelegate = KTAPI.shareInstance
var accountKit:KTAccountKitDelegate = AKFAccountKit(responseType: .accessTok
var fb:KTFBLoginManagerDelegate = FBLogInManager()
}
class LoginManager{
static let shareInstance = LoginManager()
weak var delegate:LoginManagerDelegate?
private let inject:LoginManagerInject
init(inject:LoginManagerInject = LoginManagerInject() ){
self.inject = inject
}
}
self.injec.loginAPIself.loginAPI
inject init function
26
User
UserInfo
UserSetting
User Info save in memory
Ex:User ID, Member Ship
User Setting save in NSUserDefault
Ex:IsShowXXPage
When UserInfo change , some UserSetting Variable also change
Requirement
AppDelegate
27
class UserInfo {
private var userSetting:UserSettingDelegate
init(userSetting:UserSettingDelegate = shareAppDelegate.user.setting){
self.userSetting = userSetting
}
}
User UserInfoAppDelegat
User UserSettingAppDelegate
28
class UserInfo {
private var userSetting:UserSettingDelegate
init(userSetting:UserSettingDelegate = shareAppDelegate.user.setting){
self.userSetting = userSetting
}
}
User UserInfoAppDelegat
User
UserSetting
AppDelegate
UserInfo
29
class UserInfo {
private var userSetting:UserSettingDelegate
init(userSetting:UserSettingDelegate = shareAppDelegate.user.setting){
self.userSetting = userSetting
}
}
User UserInfoAppDelegat
User
UserSetting
AppDelegate
UserInfo
User UserSettingAppDelegate
30
class UserInfo:{
private var injectFunc:()->UserSetting
private var setting:UserSetting {
return self.injectFunc()
}
init(injectFunc:@escaping ()->UserSetting =
{return shareDelegate.user.setting}){
self.injectFunc = injectFunc
}
}
30
class UserInfo:{
private var injectFunc:()->UserSetting
private var setting:UserSetting {
return self.injectFunc()
}
init(injectFunc:@escaping ()->UserSetting =
{return shareDelegate.user.setting}){
self.injectFunc = injectFunc
}
}
stored property
30
class UserInfo:{
private var injectFunc:()->UserSetting
private var setting:UserSetting {
return self.injectFunc()
}
init(injectFunc:@escaping ()->UserSetting =
{return shareDelegate.user.setting}){
self.injectFunc = injectFunc
}
}
stored property
computed property
30
class UserInfo:{
private var injectFunc:()->UserSetting
private var setting:UserSetting {
return self.injectFunc()
}
init(injectFunc:@escaping ()->UserSetting =
{return shareDelegate.user.setting}){
self.injectFunc = injectFunc
}
}
self.setting
stored property
computed property
31
殘酷的事實
花了了⼀一倍的時間寫 Feature,但是邏輯都混在⼀一起沒有拆 module,
要 refactor + 補 Unit Test 的時間,⼤大概就跟原本 Feature 的時間⼀一樣
32
循環
寫了了⼀一個 Feature,但是邏輯都混在⼀一起沒有拆 module
要補 Unit Test 好累喔,要 refactor 好危險
不敢動,不知道怎麼拆 module
有了了新 Request
寫了了⼀一個新Feature,但是邏輯都混在⼀一起沒有拆 module
…………….
33
今天開始寫 Unit Test
• 列列出 priority
• 絕對不可以錯的 module + 會⼤大量量 refuse 的 model
• 下次寫新 module 的時候先想 input output
• 做了了⼀一堆 feature ,User 可能會喜歡可能不會喜歡,

但是重要功能出 bug,那就死定了了

More Related Content

What's hot

Testing In App Billing
Testing In App BillingTesting In App Billing
Testing In App BillingRyan Harter
 
How to build a chat application with react js, nodejs, and socket.io
How to build a chat application with react js, nodejs, and socket.ioHow to build a chat application with react js, nodejs, and socket.io
How to build a chat application with react js, nodejs, and socket.ioKaty Slemon
 
RichFaces 4: Rich Ajax Components For Your JSF Applications
RichFaces 4: Rich Ajax Components For Your JSF ApplicationsRichFaces 4: Rich Ajax Components For Your JSF Applications
RichFaces 4: Rich Ajax Components For Your JSF ApplicationsMax Katz
 
Introduction Yii Framework
Introduction Yii FrameworkIntroduction Yii Framework
Introduction Yii FrameworkTuan Nguyen
 
10 reasons to choose the yii framework
10 reasons to choose the yii framework10 reasons to choose the yii framework
10 reasons to choose the yii frameworkjananya213
 
Ajax Applications with JSF 2 and New RichFaces 4 - TSSJS
Ajax Applications with JSF 2 and New RichFaces 4 - TSSJSAjax Applications with JSF 2 and New RichFaces 4 - TSSJS
Ajax Applications with JSF 2 and New RichFaces 4 - TSSJSMax Katz
 
Social Gold in-Flash Webinar Jan 2010
Social Gold in-Flash Webinar Jan 2010Social Gold in-Flash Webinar Jan 2010
Social Gold in-Flash Webinar Jan 2010Social Gold
 
JSF 2.0 (JavaEE Webinar)
JSF 2.0 (JavaEE Webinar)JSF 2.0 (JavaEE Webinar)
JSF 2.0 (JavaEE Webinar)Roger Kitain
 
IPaste SDK v.1.0
IPaste SDK v.1.0IPaste SDK v.1.0
IPaste SDK v.1.0xrebyc
 
RESTful API Design & Implementation with CodeIgniter PHP Framework
RESTful API Design & Implementation with CodeIgniter PHP FrameworkRESTful API Design & Implementation with CodeIgniter PHP Framework
RESTful API Design & Implementation with CodeIgniter PHP FrameworkBo-Yi Wu
 
What You Need To Build Cool Enterprise Applications With JSF
What You Need To Build Cool Enterprise Applications With JSFWhat You Need To Build Cool Enterprise Applications With JSF
What You Need To Build Cool Enterprise Applications With JSFMax Katz
 
Self isssued-idp
Self isssued-idpSelf isssued-idp
Self isssued-idpNov Matake
 

What's hot (14)

Testing In App Billing
Testing In App BillingTesting In App Billing
Testing In App Billing
 
Distributed banking system using rmi project
Distributed banking system using rmi projectDistributed banking system using rmi project
Distributed banking system using rmi project
 
How to build a chat application with react js, nodejs, and socket.io
How to build a chat application with react js, nodejs, and socket.ioHow to build a chat application with react js, nodejs, and socket.io
How to build a chat application with react js, nodejs, and socket.io
 
RichFaces 4: Rich Ajax Components For Your JSF Applications
RichFaces 4: Rich Ajax Components For Your JSF ApplicationsRichFaces 4: Rich Ajax Components For Your JSF Applications
RichFaces 4: Rich Ajax Components For Your JSF Applications
 
Introduction Yii Framework
Introduction Yii FrameworkIntroduction Yii Framework
Introduction Yii Framework
 
10 reasons to choose the yii framework
10 reasons to choose the yii framework10 reasons to choose the yii framework
10 reasons to choose the yii framework
 
Webauthn Tutorial
Webauthn TutorialWebauthn Tutorial
Webauthn Tutorial
 
Ajax Applications with JSF 2 and New RichFaces 4 - TSSJS
Ajax Applications with JSF 2 and New RichFaces 4 - TSSJSAjax Applications with JSF 2 and New RichFaces 4 - TSSJS
Ajax Applications with JSF 2 and New RichFaces 4 - TSSJS
 
Social Gold in-Flash Webinar Jan 2010
Social Gold in-Flash Webinar Jan 2010Social Gold in-Flash Webinar Jan 2010
Social Gold in-Flash Webinar Jan 2010
 
JSF 2.0 (JavaEE Webinar)
JSF 2.0 (JavaEE Webinar)JSF 2.0 (JavaEE Webinar)
JSF 2.0 (JavaEE Webinar)
 
IPaste SDK v.1.0
IPaste SDK v.1.0IPaste SDK v.1.0
IPaste SDK v.1.0
 
RESTful API Design & Implementation with CodeIgniter PHP Framework
RESTful API Design & Implementation with CodeIgniter PHP FrameworkRESTful API Design & Implementation with CodeIgniter PHP Framework
RESTful API Design & Implementation with CodeIgniter PHP Framework
 
What You Need To Build Cool Enterprise Applications With JSF
What You Need To Build Cool Enterprise Applications With JSFWhat You Need To Build Cool Enterprise Applications With JSF
What You Need To Build Cool Enterprise Applications With JSF
 
Self isssued-idp
Self isssued-idpSelf isssued-idp
Self isssued-idp
 

Similar to Refactor code to testable

Page Objects Done Right - selenium conference 2014
Page Objects Done Right - selenium conference 2014Page Objects Done Right - selenium conference 2014
Page Objects Done Right - selenium conference 2014Oren Rubin
 
Functional programming in C#
Functional programming in C#Functional programming in C#
Functional programming in C#Thomas Jaskula
 
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...Stanfy
 
Lab StepsSTEP 1 Login Form1. In order to do this lab, we need.docx
Lab StepsSTEP 1 Login Form1. In order to do this lab, we need.docxLab StepsSTEP 1 Login Form1. In order to do this lab, we need.docx
Lab StepsSTEP 1 Login Form1. In order to do this lab, we need.docxsmile790243
 
20200815 inversions
20200815 inversions20200815 inversions
20200815 inversionsChiwon Song
 
Application Frameworks - The Good, The Bad & The Ugly
Application Frameworks - The Good, The Bad & The UglyApplication Frameworks - The Good, The Bad & The Ugly
Application Frameworks - The Good, The Bad & The UglyRichard Lord
 
Implementing auto complete using JQuery
Implementing auto complete using JQueryImplementing auto complete using JQuery
Implementing auto complete using JQueryBhushan Mulmule
 
MEF Deep Dive by Piotr Wlodek
MEF Deep Dive by Piotr WlodekMEF Deep Dive by Piotr Wlodek
MEF Deep Dive by Piotr Wlodekinfusiondev
 
Java Web Programming [8/9] : JSF and AJAX
Java Web Programming [8/9] : JSF and AJAXJava Web Programming [8/9] : JSF and AJAX
Java Web Programming [8/9] : JSF and AJAXIMC Institute
 
Functional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftFunctional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftRodrigo Leite
 
BANK MANAGEMNT SYSTEM.pptx
BANK MANAGEMNT SYSTEM.pptxBANK MANAGEMNT SYSTEM.pptx
BANK MANAGEMNT SYSTEM.pptxVjVj28
 
OpenCms Days 2014 - User Generated Content in OpenCms 9.5
OpenCms Days 2014 - User Generated Content in OpenCms 9.5OpenCms Days 2014 - User Generated Content in OpenCms 9.5
OpenCms Days 2014 - User Generated Content in OpenCms 9.5Alkacon Software GmbH & Co. KG
 
Dependency Injection, Zend Framework and Symfony Container
Dependency Injection, Zend Framework and Symfony ContainerDependency Injection, Zend Framework and Symfony Container
Dependency Injection, Zend Framework and Symfony ContainerDiego Lewin
 
Devoxx 09 (Belgium)
Devoxx 09 (Belgium)Devoxx 09 (Belgium)
Devoxx 09 (Belgium)Roger Kitain
 
How to Validate Form With Flutter BLoC.pptx
How to Validate Form With Flutter BLoC.pptxHow to Validate Form With Flutter BLoC.pptx
How to Validate Form With Flutter BLoC.pptxBOSC Tech Labs
 

Similar to Refactor code to testable (20)

Page Objects Done Right - selenium conference 2014
Page Objects Done Right - selenium conference 2014Page Objects Done Right - selenium conference 2014
Page Objects Done Right - selenium conference 2014
 
iOS_Presentation
iOS_PresentationiOS_Presentation
iOS_Presentation
 
Functional programming in C#
Functional programming in C#Functional programming in C#
Functional programming in C#
 
Facebook api
Facebook apiFacebook api
Facebook api
 
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
 
Lab StepsSTEP 1 Login Form1. In order to do this lab, we need.docx
Lab StepsSTEP 1 Login Form1. In order to do this lab, we need.docxLab StepsSTEP 1 Login Form1. In order to do this lab, we need.docx
Lab StepsSTEP 1 Login Form1. In order to do this lab, we need.docx
 
Os Johnson
Os JohnsonOs Johnson
Os Johnson
 
20200815 inversions
20200815 inversions20200815 inversions
20200815 inversions
 
Application Frameworks - The Good, The Bad & The Ugly
Application Frameworks - The Good, The Bad & The UglyApplication Frameworks - The Good, The Bad & The Ugly
Application Frameworks - The Good, The Bad & The Ugly
 
Yii php framework_honey
Yii php framework_honeyYii php framework_honey
Yii php framework_honey
 
Implementing auto complete using JQuery
Implementing auto complete using JQueryImplementing auto complete using JQuery
Implementing auto complete using JQuery
 
MEF Deep Dive by Piotr Wlodek
MEF Deep Dive by Piotr WlodekMEF Deep Dive by Piotr Wlodek
MEF Deep Dive by Piotr Wlodek
 
Java Web Programming [8/9] : JSF and AJAX
Java Web Programming [8/9] : JSF and AJAXJava Web Programming [8/9] : JSF and AJAX
Java Web Programming [8/9] : JSF and AJAX
 
Functional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftFunctional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwift
 
BANK MANAGEMNT SYSTEM.pptx
BANK MANAGEMNT SYSTEM.pptxBANK MANAGEMNT SYSTEM.pptx
BANK MANAGEMNT SYSTEM.pptx
 
OpenCms Days 2014 - User Generated Content in OpenCms 9.5
OpenCms Days 2014 - User Generated Content in OpenCms 9.5OpenCms Days 2014 - User Generated Content in OpenCms 9.5
OpenCms Days 2014 - User Generated Content in OpenCms 9.5
 
Dependency Injection, Zend Framework and Symfony Container
Dependency Injection, Zend Framework and Symfony ContainerDependency Injection, Zend Framework and Symfony Container
Dependency Injection, Zend Framework and Symfony Container
 
Spring Security.ppt
Spring Security.pptSpring Security.ppt
Spring Security.ppt
 
Devoxx 09 (Belgium)
Devoxx 09 (Belgium)Devoxx 09 (Belgium)
Devoxx 09 (Belgium)
 
How to Validate Form With Flutter BLoC.pptx
How to Validate Form With Flutter BLoC.pptxHow to Validate Form With Flutter BLoC.pptx
How to Validate Form With Flutter BLoC.pptx
 

More from Hokila Jan

Tracking Event validator
Tracking Event validatorTracking Event validator
Tracking Event validatorHokila Jan
 
Preparation for wwdc and not waste it
Preparation for wwdc and not waste itPreparation for wwdc and not waste it
Preparation for wwdc and not waste itHokila Jan
 
Third party module strategy
Third party module strategyThird party module strategy
Third party module strategyHokila Jan
 
SwiftyJSON 慘痛經驗
SwiftyJSON   慘痛經驗SwiftyJSON   慘痛經驗
SwiftyJSON 慘痛經驗Hokila Jan
 
How to cheat jb detector and detect cheating
How to cheat jb detector and detect cheatingHow to cheat jb detector and detect cheating
How to cheat jb detector and detect cheatingHokila Jan
 
進擊的帳單
進擊的帳單進擊的帳單
進擊的帳單Hokila Jan
 
讓你的App優雅的crash三部曲
讓你的App優雅的crash三部曲讓你的App優雅的crash三部曲
讓你的App優雅的crash三部曲Hokila Jan
 
iOS app security
iOS app security  iOS app security
iOS app security Hokila Jan
 
接案公司的日子
接案公司的日子接案公司的日子
接案公司的日子Hokila Jan
 
快思慢想Ch13 14
快思慢想Ch13 14快思慢想Ch13 14
快思慢想Ch13 14Hokila Jan
 
從Scrum到放棄scrum
從Scrum到放棄scrum從Scrum到放棄scrum
從Scrum到放棄scrumHokila Jan
 
快思慢想讀書會Ch9 10
快思慢想讀書會Ch9 10快思慢想讀書會Ch9 10
快思慢想讀書會Ch9 10Hokila Jan
 

More from Hokila Jan (12)

Tracking Event validator
Tracking Event validatorTracking Event validator
Tracking Event validator
 
Preparation for wwdc and not waste it
Preparation for wwdc and not waste itPreparation for wwdc and not waste it
Preparation for wwdc and not waste it
 
Third party module strategy
Third party module strategyThird party module strategy
Third party module strategy
 
SwiftyJSON 慘痛經驗
SwiftyJSON   慘痛經驗SwiftyJSON   慘痛經驗
SwiftyJSON 慘痛經驗
 
How to cheat jb detector and detect cheating
How to cheat jb detector and detect cheatingHow to cheat jb detector and detect cheating
How to cheat jb detector and detect cheating
 
進擊的帳單
進擊的帳單進擊的帳單
進擊的帳單
 
讓你的App優雅的crash三部曲
讓你的App優雅的crash三部曲讓你的App優雅的crash三部曲
讓你的App優雅的crash三部曲
 
iOS app security
iOS app security  iOS app security
iOS app security
 
接案公司的日子
接案公司的日子接案公司的日子
接案公司的日子
 
快思慢想Ch13 14
快思慢想Ch13 14快思慢想Ch13 14
快思慢想Ch13 14
 
從Scrum到放棄scrum
從Scrum到放棄scrum從Scrum到放棄scrum
從Scrum到放棄scrum
 
快思慢想讀書會Ch9 10
快思慢想讀書會Ch9 10快思慢想讀書會Ch9 10
快思慢想讀書會Ch9 10
 

Recently uploaded

The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...ranjana rawat
 
SPICE PARK APR2024 ( 6,793 SPICE Models )
SPICE PARK APR2024 ( 6,793 SPICE Models )SPICE PARK APR2024 ( 6,793 SPICE Models )
SPICE PARK APR2024 ( 6,793 SPICE Models )Tsuyoshi Horigome
 
High Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur EscortsHigh Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur High Profile
 
Microscopic Analysis of Ceramic Materials.pptx
Microscopic Analysis of Ceramic Materials.pptxMicroscopic Analysis of Ceramic Materials.pptx
Microscopic Analysis of Ceramic Materials.pptxpurnimasatapathy1234
 
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur High Profile
 
UNIT-V FMM.HYDRAULIC TURBINE - Construction and working
UNIT-V FMM.HYDRAULIC TURBINE - Construction and workingUNIT-V FMM.HYDRAULIC TURBINE - Construction and working
UNIT-V FMM.HYDRAULIC TURBINE - Construction and workingrknatarajan
 
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...ranjana rawat
 
247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt
247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt
247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).pptssuser5c9d4b1
 
HARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICS
HARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICSHARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICS
HARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICSRajkumarAkumalla
 
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...Dr.Costas Sachpazis
 
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Christo Ananth
 
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...Dr.Costas Sachpazis
 
Software Development Life Cycle By Team Orange (Dept. of Pharmacy)
Software Development Life Cycle By  Team Orange (Dept. of Pharmacy)Software Development Life Cycle By  Team Orange (Dept. of Pharmacy)
Software Development Life Cycle By Team Orange (Dept. of Pharmacy)Suman Mia
 
Model Call Girl in Narela Delhi reach out to us at 🔝8264348440🔝
Model Call Girl in Narela Delhi reach out to us at 🔝8264348440🔝Model Call Girl in Narela Delhi reach out to us at 🔝8264348440🔝
Model Call Girl in Narela Delhi reach out to us at 🔝8264348440🔝soniya singh
 
IMPLICATIONS OF THE ABOVE HOLISTIC UNDERSTANDING OF HARMONY ON PROFESSIONAL E...
IMPLICATIONS OF THE ABOVE HOLISTIC UNDERSTANDING OF HARMONY ON PROFESSIONAL E...IMPLICATIONS OF THE ABOVE HOLISTIC UNDERSTANDING OF HARMONY ON PROFESSIONAL E...
IMPLICATIONS OF THE ABOVE HOLISTIC UNDERSTANDING OF HARMONY ON PROFESSIONAL E...RajaP95
 
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur EscortsHigh Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escortsranjana rawat
 
the ladakh protest in leh ladakh 2024 sonam wangchuk.pptx
the ladakh protest in leh ladakh 2024 sonam wangchuk.pptxthe ladakh protest in leh ladakh 2024 sonam wangchuk.pptx
the ladakh protest in leh ladakh 2024 sonam wangchuk.pptxhumanexperienceaaa
 
(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...
(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...
(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...ranjana rawat
 

Recently uploaded (20)

The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
 
SPICE PARK APR2024 ( 6,793 SPICE Models )
SPICE PARK APR2024 ( 6,793 SPICE Models )SPICE PARK APR2024 ( 6,793 SPICE Models )
SPICE PARK APR2024 ( 6,793 SPICE Models )
 
High Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur EscortsHigh Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur Escorts
 
Microscopic Analysis of Ceramic Materials.pptx
Microscopic Analysis of Ceramic Materials.pptxMicroscopic Analysis of Ceramic Materials.pptx
Microscopic Analysis of Ceramic Materials.pptx
 
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
 
DJARUM4D - SLOT GACOR ONLINE | SLOT DEMO ONLINE
DJARUM4D - SLOT GACOR ONLINE | SLOT DEMO ONLINEDJARUM4D - SLOT GACOR ONLINE | SLOT DEMO ONLINE
DJARUM4D - SLOT GACOR ONLINE | SLOT DEMO ONLINE
 
UNIT-V FMM.HYDRAULIC TURBINE - Construction and working
UNIT-V FMM.HYDRAULIC TURBINE - Construction and workingUNIT-V FMM.HYDRAULIC TURBINE - Construction and working
UNIT-V FMM.HYDRAULIC TURBINE - Construction and working
 
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
 
247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt
247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt
247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt
 
9953056974 Call Girls In South Ex, Escorts (Delhi) NCR.pdf
9953056974 Call Girls In South Ex, Escorts (Delhi) NCR.pdf9953056974 Call Girls In South Ex, Escorts (Delhi) NCR.pdf
9953056974 Call Girls In South Ex, Escorts (Delhi) NCR.pdf
 
HARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICS
HARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICSHARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICS
HARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICS
 
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
 
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
 
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...
 
Software Development Life Cycle By Team Orange (Dept. of Pharmacy)
Software Development Life Cycle By  Team Orange (Dept. of Pharmacy)Software Development Life Cycle By  Team Orange (Dept. of Pharmacy)
Software Development Life Cycle By Team Orange (Dept. of Pharmacy)
 
Model Call Girl in Narela Delhi reach out to us at 🔝8264348440🔝
Model Call Girl in Narela Delhi reach out to us at 🔝8264348440🔝Model Call Girl in Narela Delhi reach out to us at 🔝8264348440🔝
Model Call Girl in Narela Delhi reach out to us at 🔝8264348440🔝
 
IMPLICATIONS OF THE ABOVE HOLISTIC UNDERSTANDING OF HARMONY ON PROFESSIONAL E...
IMPLICATIONS OF THE ABOVE HOLISTIC UNDERSTANDING OF HARMONY ON PROFESSIONAL E...IMPLICATIONS OF THE ABOVE HOLISTIC UNDERSTANDING OF HARMONY ON PROFESSIONAL E...
IMPLICATIONS OF THE ABOVE HOLISTIC UNDERSTANDING OF HARMONY ON PROFESSIONAL E...
 
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur EscortsHigh Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
 
the ladakh protest in leh ladakh 2024 sonam wangchuk.pptx
the ladakh protest in leh ladakh 2024 sonam wangchuk.pptxthe ladakh protest in leh ladakh 2024 sonam wangchuk.pptx
the ladakh protest in leh ladakh 2024 sonam wangchuk.pptx
 
(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...
(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...
(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...
 

Refactor code to testable

  • 1. Refactor code to testable Hokila
 Cocoa heads Taipei 2017.11
  • 4. 4 現實上的難關 實際上 不會寫 Input 無法控制 (API , 不是⾃自⼰己寫的 SDK) ViewController 超級無敵⼤大
  • 5. 5 Benefit • 同樣的 code ⽤用兩兩種思考⽅方式看過 • ⾃自然⽽而然學會切 module,或者合併商業邏輯 • ⼀一次跑完幾百個幾千的 check,令⼈人安⼼心 • ⽽而且很快
  • 7. 7 LoginViewController login API AccountKit login API Facebook login API save Token after login success tracking event set User Info
  • 8. 8 問題 - dependency 太多 login API,成功 or 失敗 facebook / AccountKit SDK ⾏行行為 是否有存下 AccessToken User 資料是不是該有的都有 (UserID , MemberShip) 該有的 tracking 是不是有送 login 時 login 後
  • 10. 拆出 login manager 不知道在幹嘛的 module 叫 manager 就對了了
  • 11. 10 protocol LoginManagerDelegate:class{ func loginStart() func loginSuccess(status:LoginStatus) func loginFail(json:[String:Any]?,error:NSError?) } class LoginManager{ static let shareInstance = LoginManager() weak var delegate:LoginManagerDelegate? func loginFB() func loginAccountKit() }
  • 12. 11 class LoginController: BaseViewController{ func touchFBlogin(_ sender: AnyObject) { self.loginManager.loginFB() } func touchAClogin(_ sender: AnyObject){ self.loginManager.loginAccountKit() } } extension LoginController:LoginManagerDelegate{ func loginStart(){ //show indicator } func loginFail(json:[String:Any]?,error:NSError?){ //error handling} func loginSuccess(status:LoginStatus){ //success flow } }
  • 13. 12 LoginViewController LoginManager login API AccountKit login API Facebook login API save Token after login success tracking event set User Info login start login fail handler login success handler
  • 14. 12 LoginViewController LoginManager login API AccountKit login API Facebook login API save Token after login success tracking event set User Info login start login fail handler login success handler
  • 15. 12 LoginViewController LoginManager login API AccountKit login API Facebook login API save Token after login success tracking event set User Info login start login fail handler login success handler
  • 16. 13 Login Manager login API AccountKit login API Facebook login API LoginManagerDelegate save Token tracking event set User Info
  • 18. 15 模擬 login API ⾏行行為 protocol LoginAPIDelegate { func loginToken(token:String?,callback:@escaping loginAPICompletion) } extension API:LoginAPIDelegate{ func loginToken(token:String?,callback:@escaping loginAPICompletion{ // call real API here } }
  • 19. 16 模擬 Facebook SDK response ⾏行行為 protocol FBLoginManagerDelegate { var currentAccessToken:String? { get } …… } class FBLogInManager:FBLoginManagerDelegate { var currentAccessToken:String? { return FBSDKAccessToken.current()?.tokenString } …… }
  • 20. 17 class LoginManager{ static let shareInstance = LoginManager() weak var delegate:LoginManagerDelegate? private var loginAPI:LoginAPIDelegate private let accountKit:AccountKitDelegate private let fb:FBLoginManagerDelegate init(api:LoginAPIDelegate = API.shareInstance, ac:AccountKitDelegate = AKFAccountKit(responseType: .accessToken), fblogin:FBLoginManagerDelegate = FBLogInManager()) { self.loginAPI = api self.accountKit = ac self.fb = fblogin } } Real Instance
  • 22. 19 class MockLoginAPI:LoginAPIDelegate { var loginCallback:(LoginStatus,[String:Any]?, AccessToken?, NSError?) func loginToken(token:String?,callback:@escaping loginAPICompletion { callback(loginCallback.0,loginCallback.1, loginCallback.2,loginCallback.3) } } class MockFBLogin:FBLoginManagerDelegate { var currentAccessToken: String? }
  • 23. 20 func testFBloginSuccess() { //1.prepare input mockFBLogin.currentAccessToken = "123456789" let loginJSON = loadData(fileName: "loginSuccess", type: "json") mockLoginAPI.loginCallback = (.loginSuccess,loginJSON,authToken,nil) //2.Compose Login Manager self.loginManager = KTLoginManager(api: mockLoginAPI, ac: mockAC, fblogin: mockFBLogin) self.loginManager.delegate = mockLoginVC //3.trigger login loginManager.loginFB() //4.Verify output …….. }
  • 24. 21 func testFBloginSuccess() { …….. //4.Verify output XCTAssertTrue(mockLoginVC.isLoginSuccess) XCTAssertNotNil(mockAccessTokenManager.lastAccessToken) XCTAssertEqual(KTUser.shareUser.info.memberShip, .premium) XCTAssertNotNil(KTUser.shareUser.info.userID) XCTAssertEqual(mockTracker.lastStatus, .loginSuccess) XCTAssertEqual(mockTracker.lastLoginType, .facebook) XCTAssertNotNil(mockTracker.lastLoginUserInfo.userID) }
  • 25. dependency injection 問題 inject 的 module 太多,不確定有沒有漏
  • 26. 23 class LoginManager{ static let shareInstance = LoginManager() weak var delegate:LoginManagerDelegate? private var loginAPI:LoginAPIDelegate private let accountKit:AccountKitDelegate private let fb:FBLoginManagerDelegate init(api:LoginAPIDelegate = API.shareInstance, ac:AccountKitDelegate = AKFAccountKit(responseType: .accessToken), fblogin:FBLoginManagerDelegate = FBLogInManager()) { self.loginAPI = api self.accountKit = ac self.fb = fblogin } } Real Instance
  • 27. 24 struct LoginManagerInject{ var loginAPI:KTLoginAPIDelegate = KTAPI.shareInstance var accountKit:KTAccountKitDelegate = AKFAccountKit(responseType: .accessTok var fb:KTFBLoginManagerDelegate = FBLogInManager() } class LoginManager{ static let shareInstance = LoginManager() weak var delegate:LoginManagerDelegate? private let inject:LoginManagerInject init(inject:LoginManagerInject = LoginManagerInject() ){ self.inject = inject } }
  • 28. 24 struct LoginManagerInject{ var loginAPI:KTLoginAPIDelegate = KTAPI.shareInstance var accountKit:KTAccountKitDelegate = AKFAccountKit(responseType: .accessTok var fb:KTFBLoginManagerDelegate = FBLogInManager() } class LoginManager{ static let shareInstance = LoginManager() weak var delegate:LoginManagerDelegate? private let inject:LoginManagerInject init(inject:LoginManagerInject = LoginManagerInject() ){ self.inject = inject } } self.loginAPI
  • 29. 24 struct LoginManagerInject{ var loginAPI:KTLoginAPIDelegate = KTAPI.shareInstance var accountKit:KTAccountKitDelegate = AKFAccountKit(responseType: .accessTok var fb:KTFBLoginManagerDelegate = FBLogInManager() } class LoginManager{ static let shareInstance = LoginManager() weak var delegate:LoginManagerDelegate? private let inject:LoginManagerInject init(inject:LoginManagerInject = LoginManagerInject() ){ self.inject = inject } } self.injec.loginAPIself.loginAPI
  • 31. 26 User UserInfo UserSetting User Info save in memory Ex:User ID, Member Ship User Setting save in NSUserDefault Ex:IsShowXXPage When UserInfo change , some UserSetting Variable also change Requirement AppDelegate
  • 32. 27 class UserInfo { private var userSetting:UserSettingDelegate init(userSetting:UserSettingDelegate = shareAppDelegate.user.setting){ self.userSetting = userSetting } } User UserInfoAppDelegat User UserSettingAppDelegate
  • 33. 28 class UserInfo { private var userSetting:UserSettingDelegate init(userSetting:UserSettingDelegate = shareAppDelegate.user.setting){ self.userSetting = userSetting } } User UserInfoAppDelegat User UserSetting AppDelegate UserInfo
  • 34. 29 class UserInfo { private var userSetting:UserSettingDelegate init(userSetting:UserSettingDelegate = shareAppDelegate.user.setting){ self.userSetting = userSetting } } User UserInfoAppDelegat User UserSetting AppDelegate UserInfo User UserSettingAppDelegate
  • 35. 30 class UserInfo:{ private var injectFunc:()->UserSetting private var setting:UserSetting { return self.injectFunc() } init(injectFunc:@escaping ()->UserSetting = {return shareDelegate.user.setting}){ self.injectFunc = injectFunc } }
  • 36. 30 class UserInfo:{ private var injectFunc:()->UserSetting private var setting:UserSetting { return self.injectFunc() } init(injectFunc:@escaping ()->UserSetting = {return shareDelegate.user.setting}){ self.injectFunc = injectFunc } } stored property
  • 37. 30 class UserInfo:{ private var injectFunc:()->UserSetting private var setting:UserSetting { return self.injectFunc() } init(injectFunc:@escaping ()->UserSetting = {return shareDelegate.user.setting}){ self.injectFunc = injectFunc } } stored property computed property
  • 38. 30 class UserInfo:{ private var injectFunc:()->UserSetting private var setting:UserSetting { return self.injectFunc() } init(injectFunc:@escaping ()->UserSetting = {return shareDelegate.user.setting}){ self.injectFunc = injectFunc } } self.setting stored property computed property
  • 39. 31 殘酷的事實 花了了⼀一倍的時間寫 Feature,但是邏輯都混在⼀一起沒有拆 module, 要 refactor + 補 Unit Test 的時間,⼤大概就跟原本 Feature 的時間⼀一樣
  • 40. 32 循環 寫了了⼀一個 Feature,但是邏輯都混在⼀一起沒有拆 module 要補 Unit Test 好累喔,要 refactor 好危險 不敢動,不知道怎麼拆 module 有了了新 Request 寫了了⼀一個新Feature,但是邏輯都混在⼀一起沒有拆 module …………….
  • 41. 33 今天開始寫 Unit Test • 列列出 priority • 絕對不可以錯的 module + 會⼤大量量 refuse 的 model • 下次寫新 module 的時候先想 input output • 做了了⼀一堆 feature ,User 可能會喜歡可能不會喜歡,
 但是重要功能出 bug,那就死定了了