SlideShare ist ein Scribd-Unternehmen logo
1 von 47
Downloaden Sie, um offline zu lesen
APIClient
2018/12/14 HAKATA Test Night #1 #hakata_test_night


(@ktanaka117)
• / @ktanaka117
•
• :
• R&D Swift TDD
• PEAKS iOS
Booth iOS 

💪
APIClient
2018/12/14 HAKATA Test Night #1 #hakata_test_night


(@ktanaka117)
•
•
•
•
•
•
•
•
AccessToken/RefreshToken
AccessToken
• AccessToken AuthError
• Refresh→Retry AccessToken …
• 5
•
•
• 1 1APICall API
• Auth Error retry 1
• 5
•
•
•
• Etc…
Android 

OkHttp
•
• iOS
•
iOS


• Interactor - APIClient SessionManager
• Refresh Request
• Refresh Request
• APIKit→Alamofire
• APIKit Refresh/Retry
• Request-Completion 

Alamofire
: RequestRetrier/RequestAdapter
: RequestRetrier/RequestAdapter
• APIClient
• APIClient
•
• 1
• 2
• 2
• HTTP Status
- 1
•
• [200]
• [400]
• [401] refresh 

- 1
•
• [200]
• [400]
• [401]
• [401, 200(refresh), 200(retry)]
• [401, 200(refresh), 400(retry)]
• [401, 200(refresh), 401(retry)]
Refresh/Retry 

- 2
•
• [200(a), 200(b)] (ab )
• [400(a), 400(b)] (ab )
• [401(a), 401(b)]
- 2
•
• [200(a), 200(b)] / [400(a), 400(b)] (ab )
• [401(a), 200(a, refresh), 200(a, retry), 200(b)] (ab )
• [401(a), 200(a, refresh), 400(a, retry), 200(b)] (ab )
• [401(a), 400(a, refresh), 401(b)] (ab )
• [401(a), 401(a, refresh), 401(b)] (ab )
- 2
•
- 2
•
• [200(a), 200(b)] / [400(a), 400(b)] (ab )
• [400(a), 401(b)] / [200(a) / 400(b)] (ab , 1 )
• [401(a), 401(b), 200(a, refresh), 200(a, retry), 200(b, retry)] (b Refresh )
• [401(a), 200(a, refresh), 401(b), 200(a, retry), 200(b, retry)] (b Refresh )
• etc…


• Stub
• XCTContext
• AccessToken, RefreshToken
• AccessToken, RefreshToken
• Stub
• XCTContext
• AccessToken, RefreshToken
• AccessToken, RefreshToken
🙇
Stub
var responses = [authError, successWithAccessToken, success]

stub(condition: isHost("example.com")) { request in
return responses.removeFirst()
}
// ......
// ...
OHHTTPStubs.removeAllStubs()
responses test function …
Stub
HTTPStub.activate(
condition: isHost("example.com"),
responses: [authError, successWithAccessToken, success])
// ......
// ...
HTTPStub.deactivate()
responses 

Stub
struct HTTPStub {
static func activate(condition: @escaping OHHTTPStubsTestBlock,
responses: [OHHTTPStubsResponse]) {
var responsesArray = responses
stub(condition: condition) { request in
let response = responsesArray.removeFirst()
return response
}
}
static func deactivate() {
OHHTTPStubs.removeAllStubs()
}
}
Blog
XCTContext
XCTContext.runActivity(named: case1) { [weak self] _ in
HTTPStub.activate(responses: [authError, successWithAccesstoken, success])
expectation.expectedFulfillmentCount = 1

sessionManager.requestData(TestRequest())
.subscribe(onSuccess: { data, _ in
let success = try? JSONDecoder().decodeSuccess(from: data)
XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: "Success"))
expectation.fulfill()
}, onError: { error in
XCTFail(error.localizedDescription)
expectation.fulfill()
}).disposed(by: disposeBag)
self?.waitForExpectations(timeout: 1.0)
clean()
}
XCTContext
XCTContext.runActivity(named: case1) { [weak self] _ in
HTTPStub.activate(responses: [authError, successWithAccesstoken, success])
expectation.expectedFulfillmentCount = 1

sessionManager.requestData(TestRequest())
.subscribe(onSuccess: { data, _ in
let success = try? JSONDecoder().decodeSuccess(from: data)
XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: "Success"))
expectation.fulfill()
}, onError: { error in
XCTFail(error.localizedDescription)
expectation.fulfill()
}).disposed(by: disposeBag)
self?.waitForExpectations(timeout: 1.0)
clean()
}
👃
XCTContext
XCTContext.runActivity(named: case1) { [weak self] _ in
HTTPStub.activate(responses: [authError, successWithAccesstoken, success])
expectation.expectedFulfillmentCount = 1

sessionManager.requestData(TestRequest())
.subscribe(onSuccess: { data, _ in
let success = try? JSONDecoder().decodeSuccess(from: data)
XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: "Success"))
expectation.fulfill()
}, onError: { error in
XCTFail(error.localizedDescription)
expectation.fulfill()
}).disposed(by: disposeBag)
self?.waitForExpectations(timeout: 1.0)
clean()
}
Stub 👀
XCTContext
XCTContext.runActivity(named: case1) { [weak self] _ in
HTTPStub.activate(responses: [authError, successWithAccesstoken, success])
expectation.expectedFulfillmentCount = 1

sessionManager.requestData(TestRequest())
.subscribe(onSuccess: { data, _ in
let success = try? JSONDecoder().decodeSuccess(from: data)
XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: "Success"))
expectation.fulfill()
}, onError: { error in
XCTFail(error.localizedDescription)
expectation.fulfill()
}).disposed(by: disposeBag)
self?.waitForExpectations(timeout: 1.0)
clean()
}
expectation 👀
XCTContext
XCTContext.runActivity(named: case1) { [weak self] _ in
HTTPStub.activate(responses: [authError, successWithAccesstoken, success])
expectation.expectedFulfillmentCount = 1

sessionManager.requestData(TestRequest())
.subscribe(onSuccess: { data, _ in
let success = try? JSONDecoder().decodeSuccess(from: data)
XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: "Success"))
expectation.fulfill()
}, onError: { error in
XCTFail(error.localizedDescription)
expectation.fulfill()
}).disposed(by: disposeBag)
self?.waitForExpectations(timeout: 1.0)
clean()
} 👀
XCTContext
XCTContext.runActivity(named: case1) { [weak self] _ in
HTTPStub.activate(responses: [authError, successWithAccesstoken, success])
expectation.expectedFulfillmentCount = 1

sessionManager.requestData(TestRequest())
.subscribe(onSuccess: { data, _ in
let success = try? JSONDecoder().decodeSuccess(from: data)
XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: “Success"))
expectation.fulfill()
}, onError: { error in
XCTFail(error.localizedDescription)
expectation.fulfill()
}).disposed(by: disposeBag)
self?.waitForExpectations(timeout: 1.0)
clean()
}
👀
XCTContext
extension XCTContext {
static func runWaitActivity(testCase: XCTestCase,
name name: String,
timeout: TimeInterval = 1.0,
prepare: (() -> Void)? = nil,
then: (XCTestExpectation) -> Void,
clean: (() -> Void)? = nil) {
runActivity(named: named) { _ in
let expectation = name
prepare?()
then(expectation)
testCase.waitForExpectations(timeout: timeout, handler: nil)
clean?()
}
}
}
XCTContext
XCTContext
.runWaitActivity(testCase: self, named: case1, prepare: {
HTTPStub.activate(responses: [authError, successWithAccesstoken, success])
}, then: { expectation in
expectation.expectedFulfillmentCount = 1
sessionManager.requestData(TestRequest())
.subscribe(onSuccess: { data, _ in
let success = try? JSONDecoder().decodeSuccess(from: data)
XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: "Success"))
expectation.fulfill()
}, onError: { error in
XCTFail(error.localizedDescription)
expectation.fulfill()
})
.disposed(by: disposeBag)
}, clean: clean)


😄
•
• 

•

Weitere ähnliche Inhalte

Was ist angesagt?

Automate that
Automate thatAutomate that
Automate that
Atlassian
 
JIRA REST Client for Python - Atlassian Summit 2012
JIRA REST Client for Python - Atlassian Summit 2012JIRA REST Client for Python - Atlassian Summit 2012
JIRA REST Client for Python - Atlassian Summit 2012
Atlassian
 

Was ist angesagt? (20)

JavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good PartsJavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good Parts
 
Advanced Durable Functions - Serverless Meetup Tokyo - Feb 2018
Advanced Durable Functions - Serverless Meetup Tokyo - Feb 2018Advanced Durable Functions - Serverless Meetup Tokyo - Feb 2018
Advanced Durable Functions - Serverless Meetup Tokyo - Feb 2018
 
Azure Durable Functions (2019-03-30)
Azure Durable Functions (2019-03-30) Azure Durable Functions (2019-03-30)
Azure Durable Functions (2019-03-30)
 
Durable functions 2.0 (2019-10-10)
Durable functions 2.0 (2019-10-10)Durable functions 2.0 (2019-10-10)
Durable functions 2.0 (2019-10-10)
 
Serverless APIs, the Good, the Bad and the Ugly (2019-09-19)
Serverless APIs, the Good, the Bad and the Ugly (2019-09-19)Serverless APIs, the Good, the Bad and the Ugly (2019-09-19)
Serverless APIs, the Good, the Bad and the Ugly (2019-09-19)
 
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
Node.js: Continuation-Local-Storage and the Magic of AsyncListenerNode.js: Continuation-Local-Storage and the Magic of AsyncListener
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
 
(1) c sharp introduction_basics_dot_net
(1) c sharp introduction_basics_dot_net(1) c sharp introduction_basics_dot_net
(1) c sharp introduction_basics_dot_net
 
Azure Durable Functions (2018-06-13)
Azure Durable Functions (2018-06-13)Azure Durable Functions (2018-06-13)
Azure Durable Functions (2018-06-13)
 
Software engineering ⊇ Software testing
Software engineering ⊇ Software testingSoftware engineering ⊇ Software testing
Software engineering ⊇ Software testing
 
Automate that
Automate thatAutomate that
Automate that
 
The Road To Reactive with RxJava JEEConf 2016
The Road To Reactive with RxJava JEEConf 2016The Road To Reactive with RxJava JEEConf 2016
The Road To Reactive with RxJava JEEConf 2016
 
JIRA REST Client for Python - Atlassian Summit 2012
JIRA REST Client for Python - Atlassian Summit 2012JIRA REST Client for Python - Atlassian Summit 2012
JIRA REST Client for Python - Atlassian Summit 2012
 
#ajn3.lt.marblejenka
#ajn3.lt.marblejenka#ajn3.lt.marblejenka
#ajn3.lt.marblejenka
 
Tech Talks_25.04.15_Session 3_Tibor Sulyan_Distributed coordination with zook...
Tech Talks_25.04.15_Session 3_Tibor Sulyan_Distributed coordination with zook...Tech Talks_25.04.15_Session 3_Tibor Sulyan_Distributed coordination with zook...
Tech Talks_25.04.15_Session 3_Tibor Sulyan_Distributed coordination with zook...
 
Finding the right stuff, an intro to Elasticsearch (at Rug::B)
Finding the right stuff, an intro to Elasticsearch (at Rug::B) Finding the right stuff, an intro to Elasticsearch (at Rug::B)
Finding the right stuff, an intro to Elasticsearch (at Rug::B)
 
Reactive Thinking in Java
Reactive Thinking in JavaReactive Thinking in Java
Reactive Thinking in Java
 
SolrJ: Power and Pitfalls - Jason Gerlowski, Lucidworks
SolrJ: Power and Pitfalls - Jason Gerlowski, LucidworksSolrJ: Power and Pitfalls - Jason Gerlowski, Lucidworks
SolrJ: Power and Pitfalls - Jason Gerlowski, Lucidworks
 
Automating OWASP ZAP - DevCSecCon talk
Automating OWASP ZAP - DevCSecCon talk Automating OWASP ZAP - DevCSecCon talk
Automating OWASP ZAP - DevCSecCon talk
 
FullStack Reativo com Spring WebFlux + Angular
FullStack Reativo com Spring WebFlux + AngularFullStack Reativo com Spring WebFlux + Angular
FullStack Reativo com Spring WebFlux + Angular
 
Nestjs MasterClass Slides
Nestjs MasterClass SlidesNestjs MasterClass Slides
Nestjs MasterClass Slides
 

Ähnlich wie トークンリフレッシュ処理を含むAPIClientのテスト #hakata_test_night

Anton Moldovan "Load testing which you always wanted"
Anton Moldovan "Load testing which you always wanted"Anton Moldovan "Load testing which you always wanted"
Anton Moldovan "Load testing which you always wanted"
Fwdays
 
Salesforce Batch processing - Atlanta SFUG
Salesforce Batch processing - Atlanta SFUGSalesforce Batch processing - Atlanta SFUG
Salesforce Batch processing - Atlanta SFUG
vraopolisetti
 
Codestrong 2012 breakout session hacking titanium
Codestrong 2012 breakout session   hacking titaniumCodestrong 2012 breakout session   hacking titanium
Codestrong 2012 breakout session hacking titanium
Axway Appcelerator
 
Make your gui shine with ajax solr
Make your gui shine with ajax solrMake your gui shine with ajax solr
Make your gui shine with ajax solr
lucenerevolution
 
Introduction to Node.js
Introduction to Node.jsIntroduction to Node.js
Introduction to Node.js
Richard Lee
 

Ähnlich wie トークンリフレッシュ処理を含むAPIClientのテスト #hakata_test_night (20)

Phoenix + Reactで 社内システムを 密かに作ってる
Phoenix + Reactで 社内システムを 密かに作ってるPhoenix + Reactで 社内システムを 密かに作ってる
Phoenix + Reactで 社内システムを 密かに作ってる
 
ERRest - Designing a good REST service
ERRest - Designing a good REST serviceERRest - Designing a good REST service
ERRest - Designing a good REST service
 
[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless
 
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
 
HashiCorp Vault Plugin Infrastructure
HashiCorp Vault Plugin InfrastructureHashiCorp Vault Plugin Infrastructure
HashiCorp Vault Plugin Infrastructure
 
Nordic APIs - Automatic Testing of (RESTful) API Documentation
Nordic APIs - Automatic Testing of (RESTful) API DocumentationNordic APIs - Automatic Testing of (RESTful) API Documentation
Nordic APIs - Automatic Testing of (RESTful) API Documentation
 
Velocity EU 2014 — Offline-first web apps
Velocity EU 2014 — Offline-first web appsVelocity EU 2014 — Offline-first web apps
Velocity EU 2014 — Offline-first web apps
 
Anton Moldovan "Load testing which you always wanted"
Anton Moldovan "Load testing which you always wanted"Anton Moldovan "Load testing which you always wanted"
Anton Moldovan "Load testing which you always wanted"
 
Socket.io
Socket.ioSocket.io
Socket.io
 
IVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & Mobile
IVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & MobileIVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & Mobile
IVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & Mobile
 
Salesforce Batch processing - Atlanta SFUG
Salesforce Batch processing - Atlanta SFUGSalesforce Batch processing - Atlanta SFUG
Salesforce Batch processing - Atlanta SFUG
 
Codestrong 2012 breakout session hacking titanium
Codestrong 2012 breakout session   hacking titaniumCodestrong 2012 breakout session   hacking titanium
Codestrong 2012 breakout session hacking titanium
 
NodeJS
NodeJSNodeJS
NodeJS
 
Curator intro
Curator introCurator intro
Curator intro
 
Tale of ISUCON and Its Bench Tools
Tale of ISUCON and Its Bench ToolsTale of ISUCON and Its Bench Tools
Tale of ISUCON and Its Bench Tools
 
Make your gui shine with ajax solr
Make your gui shine with ajax solrMake your gui shine with ajax solr
Make your gui shine with ajax solr
 
Introduction to Node.js
Introduction to Node.jsIntroduction to Node.js
Introduction to Node.js
 
Java-Jersey 到 Python-Flask 服務不中斷重構之旅
Java-Jersey 到 Python-Flask 服務不中斷重構之旅Java-Jersey 到 Python-Flask 服務不中斷重構之旅
Java-Jersey 到 Python-Flask 服務不中斷重構之旅
 
ITT 2014 - Erik Hellmann - Android Programming - Smarter and Better Networking
ITT 2014 - Erik Hellmann - Android Programming - Smarter and Better NetworkingITT 2014 - Erik Hellmann - Android Programming - Smarter and Better Networking
ITT 2014 - Erik Hellmann - Android Programming - Smarter and Better Networking
 
MLflow at Company Scale
MLflow at Company ScaleMLflow at Company Scale
MLflow at Company Scale
 

Mehr von Kenji Tanaka

リリース前のリグレッションテストがめんどい!のでMagic PodでUIテストを試してみる #pixiv_app_night
リリース前のリグレッションテストがめんどい!のでMagic PodでUIテストを試してみる #pixiv_app_nightリリース前のリグレッションテストがめんどい!のでMagic PodでUIテストを試してみる #pixiv_app_night
リリース前のリグレッションテストがめんどい!のでMagic PodでUIテストを試してみる #pixiv_app_night
Kenji Tanaka
 

Mehr von Kenji Tanaka (20)

FatViewControllerを安全に書き換える方法が見つからなかったので、どういう痛みを許容するか考えた #iosdc
FatViewControllerを安全に書き換える方法が見つからなかったので、どういう痛みを許容するか考えた #iosdcFatViewControllerを安全に書き換える方法が見つからなかったので、どういう痛みを許容するか考えた #iosdc
FatViewControllerを安全に書き換える方法が見つからなかったので、どういう痛みを許容するか考えた #iosdc
 
リリース前のリグレッションテストがめんどい!のでMagic PodでUIテストを試してみる #pixiv_app_night
リリース前のリグレッションテストがめんどい!のでMagic PodでUIテストを試してみる #pixiv_app_nightリリース前のリグレッションテストがめんどい!のでMagic PodでUIテストを試してみる #pixiv_app_night
リリース前のリグレッションテストがめんどい!のでMagic PodでUIテストを試してみる #pixiv_app_night
 
ポストモーテムやってみた #yjbonfire
ポストモーテムやってみた #yjbonfireポストモーテムやってみた #yjbonfire
ポストモーテムやってみた #yjbonfire
 
2つの同期 4つの状態 #pixiv_ios_arch
2つの同期 4つの状態 #pixiv_ios_arch2つの同期 4つの状態 #pixiv_ios_arch
2つの同期 4つの状態 #pixiv_ios_arch
 
2つの同期 4つの状態 #app_mp
2つの同期 4つの状態 #app_mp2つの同期 4つの状態 #app_mp
2つの同期 4つの状態 #app_mp
 
2つの同期 4つの状態 #roppongiswift
2つの同期 4つの状態 #roppongiswift2つの同期 4つの状態 #roppongiswift
2つの同期 4つの状態 #roppongiswift
 
よく使うテストヘルパーの紹介 #ios_test_night
よく使うテストヘルパーの紹介 #ios_test_nightよく使うテストヘルパーの紹介 #ios_test_night
よく使うテストヘルパーの紹介 #ios_test_night
 
Swiftで聞いておぼえるテスト書き
Swiftで聞いておぼえるテスト書きSwiftで聞いておぼえるテスト書き
Swiftで聞いておぼえるテスト書き
 
設計時空のリファクタリング
設計時空のリファクタリング設計時空のリファクタリング
設計時空のリファクタリング
 
WACATE 2018 Summer
WACATE 2018 SummerWACATE 2018 Summer
WACATE 2018 Summer
 
テスト駆動開発入門 by Swift
テスト駆動開発入門 by Swiftテスト駆動開発入門 by Swift
テスト駆動開発入門 by Swift
 
An iOS Engineer challenges Web.
An iOS Engineer challenges Web.An iOS Engineer challenges Web.
An iOS Engineer challenges Web.
 
エンジニアのためのブログ講座Ver4
エンジニアのためのブログ講座Ver4エンジニアのためのブログ講座Ver4
エンジニアのためのブログ講座Ver4
 
TDDやってみよ
TDDやってみよTDDやってみよ
TDDやってみよ
 
ストレス社会に生きる、iOSエンジニアにオススメする百合の世界と作品
ストレス社会に生きる、iOSエンジニアにオススメする百合の世界と作品ストレス社会に生きる、iOSエンジニアにオススメする百合の世界と作品
ストレス社会に生きる、iOSエンジニアにオススメする百合の世界と作品
 
節子、それViewControllerやない...、FatViewControllerや...。
節子、それViewControllerやない...、FatViewControllerや...。節子、それViewControllerやない...、FatViewControllerや...。
節子、それViewControllerやない...、FatViewControllerや...。
 
iOS 11からのDeviceCheck #とは
iOS 11からのDeviceCheck #とはiOS 11からのDeviceCheck #とは
iOS 11からのDeviceCheck #とは
 
設計に答えはないから探してみよう
設計に答えはないから探してみよう設計に答えはないから探してみよう
設計に答えはないから探してみよう
 
iOS 11からのアプリ間ファイル共有
iOS 11からのアプリ間ファイル共有iOS 11からのアプリ間ファイル共有
iOS 11からのアプリ間ファイル共有
 
iOS 11からのアプリ間ファイル共有_公開用
iOS 11からのアプリ間ファイル共有_公開用iOS 11からのアプリ間ファイル共有_公開用
iOS 11からのアプリ間ファイル共有_公開用
 

Kürzlich hochgeladen

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 

Kürzlich hochgeladen (20)

Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024
 

トークンリフレッシュ処理を含むAPIClientのテスト #hakata_test_night

  • 1. APIClient 2018/12/14 HAKATA Test Night #1 #hakata_test_night 
 (@ktanaka117)
  • 2. • / @ktanaka117 • • : • R&D Swift TDD • PEAKS iOS
  • 3.
  • 5. APIClient 2018/12/14 HAKATA Test Night #1 #hakata_test_night 
 (@ktanaka117)
  • 7.
  • 9. • AccessToken AuthError • Refresh→Retry AccessToken … • 5 • • • 1 1APICall API
  • 10. • Auth Error retry 1 • 5
  • 12.
  • 13.
  • 16. • Interactor - APIClient SessionManager • Refresh Request • Refresh Request • APIKit→Alamofire • APIKit Refresh/Retry • Request-Completion 
 Alamofire
  • 19.
  • 21. • 1 • 2 • 2 • HTTP Status
  • 22. - 1 • • [200] • [400] • [401] refresh 

  • 23. - 1 • • [200] • [400] • [401] • [401, 200(refresh), 200(retry)] • [401, 200(refresh), 400(retry)] • [401, 200(refresh), 401(retry)] Refresh/Retry 

  • 24. - 2 • • [200(a), 200(b)] (ab ) • [400(a), 400(b)] (ab ) • [401(a), 401(b)]
  • 25. - 2 • • [200(a), 200(b)] / [400(a), 400(b)] (ab ) • [401(a), 200(a, refresh), 200(a, retry), 200(b)] (ab ) • [401(a), 200(a, refresh), 400(a, retry), 200(b)] (ab ) • [401(a), 400(a, refresh), 401(b)] (ab ) • [401(a), 401(a, refresh), 401(b)] (ab )
  • 27. - 2 • • [200(a), 200(b)] / [400(a), 400(b)] (ab ) • [400(a), 401(b)] / [200(a) / 400(b)] (ab , 1 ) • [401(a), 401(b), 200(a, refresh), 200(a, retry), 200(b, retry)] (b Refresh ) • [401(a), 200(a, refresh), 401(b), 200(a, retry), 200(b, retry)] (b Refresh ) • etc…
  • 28.
  • 29.
  • 30.
  • 31.
  • 32. • Stub • XCTContext • AccessToken, RefreshToken • AccessToken, RefreshToken
  • 33. • Stub • XCTContext • AccessToken, RefreshToken • AccessToken, RefreshToken 🙇
  • 34. Stub var responses = [authError, successWithAccessToken, success]
 stub(condition: isHost("example.com")) { request in return responses.removeFirst() } // ...... // ... OHHTTPStubs.removeAllStubs() responses test function …
  • 35. Stub HTTPStub.activate( condition: isHost("example.com"), responses: [authError, successWithAccessToken, success]) // ...... // ... HTTPStub.deactivate() responses 

  • 36. Stub struct HTTPStub { static func activate(condition: @escaping OHHTTPStubsTestBlock, responses: [OHHTTPStubsResponse]) { var responsesArray = responses stub(condition: condition) { request in let response = responsesArray.removeFirst() return response } } static func deactivate() { OHHTTPStubs.removeAllStubs() } }
  • 37. Blog
  • 38. XCTContext XCTContext.runActivity(named: case1) { [weak self] _ in HTTPStub.activate(responses: [authError, successWithAccesstoken, success]) expectation.expectedFulfillmentCount = 1
 sessionManager.requestData(TestRequest()) .subscribe(onSuccess: { data, _ in let success = try? JSONDecoder().decodeSuccess(from: data) XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: "Success")) expectation.fulfill() }, onError: { error in XCTFail(error.localizedDescription) expectation.fulfill() }).disposed(by: disposeBag) self?.waitForExpectations(timeout: 1.0) clean() }
  • 39. XCTContext XCTContext.runActivity(named: case1) { [weak self] _ in HTTPStub.activate(responses: [authError, successWithAccesstoken, success]) expectation.expectedFulfillmentCount = 1
 sessionManager.requestData(TestRequest()) .subscribe(onSuccess: { data, _ in let success = try? JSONDecoder().decodeSuccess(from: data) XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: "Success")) expectation.fulfill() }, onError: { error in XCTFail(error.localizedDescription) expectation.fulfill() }).disposed(by: disposeBag) self?.waitForExpectations(timeout: 1.0) clean() } 👃
  • 40. XCTContext XCTContext.runActivity(named: case1) { [weak self] _ in HTTPStub.activate(responses: [authError, successWithAccesstoken, success]) expectation.expectedFulfillmentCount = 1
 sessionManager.requestData(TestRequest()) .subscribe(onSuccess: { data, _ in let success = try? JSONDecoder().decodeSuccess(from: data) XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: "Success")) expectation.fulfill() }, onError: { error in XCTFail(error.localizedDescription) expectation.fulfill() }).disposed(by: disposeBag) self?.waitForExpectations(timeout: 1.0) clean() } Stub 👀
  • 41. XCTContext XCTContext.runActivity(named: case1) { [weak self] _ in HTTPStub.activate(responses: [authError, successWithAccesstoken, success]) expectation.expectedFulfillmentCount = 1
 sessionManager.requestData(TestRequest()) .subscribe(onSuccess: { data, _ in let success = try? JSONDecoder().decodeSuccess(from: data) XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: "Success")) expectation.fulfill() }, onError: { error in XCTFail(error.localizedDescription) expectation.fulfill() }).disposed(by: disposeBag) self?.waitForExpectations(timeout: 1.0) clean() } expectation 👀
  • 42. XCTContext XCTContext.runActivity(named: case1) { [weak self] _ in HTTPStub.activate(responses: [authError, successWithAccesstoken, success]) expectation.expectedFulfillmentCount = 1
 sessionManager.requestData(TestRequest()) .subscribe(onSuccess: { data, _ in let success = try? JSONDecoder().decodeSuccess(from: data) XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: "Success")) expectation.fulfill() }, onError: { error in XCTFail(error.localizedDescription) expectation.fulfill() }).disposed(by: disposeBag) self?.waitForExpectations(timeout: 1.0) clean() } 👀
  • 43. XCTContext XCTContext.runActivity(named: case1) { [weak self] _ in HTTPStub.activate(responses: [authError, successWithAccesstoken, success]) expectation.expectedFulfillmentCount = 1
 sessionManager.requestData(TestRequest()) .subscribe(onSuccess: { data, _ in let success = try? JSONDecoder().decodeSuccess(from: data) XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: “Success")) expectation.fulfill() }, onError: { error in XCTFail(error.localizedDescription) expectation.fulfill() }).disposed(by: disposeBag) self?.waitForExpectations(timeout: 1.0) clean() } 👀
  • 44. XCTContext extension XCTContext { static func runWaitActivity(testCase: XCTestCase, name name: String, timeout: TimeInterval = 1.0, prepare: (() -> Void)? = nil, then: (XCTestExpectation) -> Void, clean: (() -> Void)? = nil) { runActivity(named: named) { _ in let expectation = name prepare?() then(expectation) testCase.waitForExpectations(timeout: timeout, handler: nil) clean?() } } }
  • 45. XCTContext XCTContext .runWaitActivity(testCase: self, named: case1, prepare: { HTTPStub.activate(responses: [authError, successWithAccesstoken, success]) }, then: { expectation in expectation.expectedFulfillmentCount = 1 sessionManager.requestData(TestRequest()) .subscribe(onSuccess: { data, _ in let success = try? JSONDecoder().decodeSuccess(from: data) XCTAssertEqual(success, OHHTTPStubsResponse.Success(data: "Success")) expectation.fulfill() }, onError: { error in XCTFail(error.localizedDescription) expectation.fulfill() }) .disposed(by: disposeBag) }, clean: clean) 
 😄
  • 46.