Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
CDC Testing on iOS
giginet
September 13, 2016
Technology
4
14k
CDC Testing on iOS
We integrated Pact for iOS applications.
http://cookpad-tech-kitchen.connpass.com/event/37908/
giginet
September 13, 2016
Tweet
Share
More Decks by giginet
See All by giginet
OSS Forward Workshop
giginet
2
700
Crossroad - 最高のCustom URL Schemeルーティングを支える技術2021
giginet
4
280
わいわいDocC ~ waiwai-docc ~
giginet
0
53
今日から使える実践的Swift Concurrency / Introducing Swift Concurrency
giginet
1
82
大規模なアプリのマルチモジュール構成の実践
giginet
4
9.8k
モバイルアプリ行動ログ基盤を”大統一”した話
giginet
0
2.2k
XcodeGen超入門
giginet
8
2.6k
👾 令和時代のゲームボーイ開発
giginet
3
6.6k
Azure Pipeline
giginet
0
1.3k
Other Decks in Technology
See All in Technology
The role of the data organization as a business progresses
line_developers
PRO
3
820
Power AutomateでのAdaptive Cards-基本編
miyakemito
1
210
2022年度新卒技術研修「 ソフトウェアテスト」講義
excitejp
PRO
0
340
What's new in Vision
satotakeshi
0
150
QiitaConference2022
fuwasegu
0
150
視座とアジャイル / shiza_and_agile
kyoshimoto
0
180
ハッカー飯に New Relic を導入して実践した3つのこと
nobuakikikuchi
0
170
誰が正解を知っているのか / Who knows the right answer
takaking22
1
220
WACATE 2022 夏 ワークショップの目的
imtnd
0
110
Data in Google I/O - IO Extended GDG Seoul
kennethanceyer
0
150
Implementing Kubernetes operators in Java with Micronaut - TechWeek Java Summit 2022
alvarosanchez
0
110
suppress-ts-errors を使って TypeScriptの型チェックを漸進的に強化する / Introducing-suppress-ts-errors
kawamataryo
2
110
Featured
See All Featured
Creatively Recalculating Your Daily Design Routine
revolveconf
207
10k
Git: the NoSQL Database
bkeepers
PRO
415
59k
Pencils Down: Stop Designing & Start Developing
hursman
112
9.8k
jQuery: Nuts, Bolts and Bling
dougneiner
56
6.4k
How To Stay Up To Date on Web Technology
chriscoyier
780
250k
Facilitating Awesome Meetings
lara
29
4k
Writing Fast Ruby
sferik
612
57k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
19
1.4k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
29
4.3k
We Have a Design System, Now What?
morganepeng
35
2.9k
Reflections from 52 weeks, 52 projects
jeffersonlam
337
17k
Robots, Beer and Maslow
schacon
152
7.1k
Transcript
CDC Testing on iOS Cookpad Tech Kitchen Vol2 @giginet
୭ʁ • @giginet • ٕज़෦ϞόΠϧج൫άϧʔϓ • ৽ଔ2
Agenda • CDC Testingͱ • Pact • Phakchi • ݟ
՝ • APIͷมߋʹΑΔαʔϏεͷഁյͷݕ • αʔόʔɺΫϥΠΞϯτ։ൃऀؒͰͷ༷ڞ ༗ͷඞཁੑ
CDC Testing
CDC Testing • Consumer-Driven Contract Testing • APIͱΫϥΠΞϯτؒͷ߹ੑΛςετ͢Δ ͨΊͷςετख๏
༻ޠ • Provider • APIΛఏڙ͢Δଆ = APIαʔόʔ • Consumer •
APIΛར༻͢Δଆ = iOSΞϓϦ • Contract • APIͷ༷
Pact • CDC TestingͷͨΊͷϑϨʔϜϫʔΫ • http://docs.pact.io/ • https://github.com/realestate-com-au/pact • ݩʑαʔόʔؒ࿈ܞΛఆ
࣮ફ Pact:ϚΠΫϩαʔϏε࣌ͷςετπʔϧ http://techlife.cookpad.com/entry/ 2016/06/28/164247
$POTVNFS ϞόΠϧΞϓϦ 1SPWJEFS "1* #SPLFS HTTP Request HTTP Response Pact
file ϦΫΤετͱϨεϙϯεΛBrokerʹه͢Δ
Contract(ྫ) • GET /recipes?keyword=ण࢘ { "recipes":[ { "name":"େτϩ", "description":"ඒຯ͍" },
{ "name":"͏ʹ", "description":"ඒຯ͍" } ] }
$POTVNFS ϞόΠϧΞϓϦ 1SPWJEFS "1* #SPLFS HTTP Request HTTP Response Pact
file Pact fileʹهड़ͨ͠ϦΫΤετΛ͛ͯݕূ͢Δ
$POTVNFS ϞόΠϧΞϓϦ 1SPWJEFS "1* #SPLFS HTTP Request HTTP Response Pact
file
6OJU5FTU 1BDU.PDL4FSWFS Ϣχοτςετͷىಈ࣌ʹϞο ΫαʔόʔΛ্ཱͪ͛Δ ىಈ
6OJU5FTU 1BDU.PDL4FSWFS ϞοΫαʔόʔʹϦΫΤετͱ Ϩεϙϯεͷ JOUFSBDUJPO Λొ ͢Δ ϦΫΤετͱ ظ͢ΔϨεϙϯεΛ ొ
6OJU5FTU 1BDU.PDL4FSWFS Ϣχοτςετ͔ΒϞοΫαʔ όʔʹରͯ͠ϦΫΤετΛૹΓɺର ʹͳΔϨεϙϯεΛฦ͢ ϦΫΤετ Ϩεϙϯε
6OJU5FTU 1BDU.PDL4FSWFS ड͚औͬͨϨεϙϯεΛΫϥΠ ΞϯτଆͰݕࠪ͠ɺਖ਼͚͠ΕϞο Ϋαʔόʔʹ͑Δ 1BDUϑΝΠϧ͕ੜ ऴྃΛ௨ ੜ Pact file
#SPLFS ϞοΫαʔόʔ͕1BDUϑΝΠϧ Λੜ͢ΔͷͰ#SPLFSʹΞοϓ ϩʔυ͢Δ Ξοϓϩʔυ Pact file
cookpad/Phakchi https://github.com/cookpad/Phakchi
Phakchi • PactͷςετέʔεΛָʹهड़͢ΔϥΠϒϥϦ • Swift • XCTest͔Βར༻ • cf :
https://github.com/DiUS/pact-consumer- swift
1. ϞοΫαʔόʔͷىಈ 2. interactionͷొ 3. ϦΫΤετΛૹΔ 4. Ϩεϙϯεͷݕࠪ 5. PactϑΝΠϧͷੜ
1. ϞοΫαʔόʔͷىಈ 2. interactionͷొ 3. ϦΫΤετΛૹΔ 4. Ϩεϙϯεͷݕࠪ 5. PactϑΝΠϧͷੜ
None
import Foundation import XCTest import Phakchi class RecipeSearchPact: XCTest {
var session: Session! override func setUp() { super.setUp() let expectation = expectationWithDescription("Set up Pact environment") controlServer.startSession(withConsumerName: "Cookpad", providerName: "APIServer") { newSession in if let newSession = newSession { self.session = newSession expectation.fulfill() } } waitForExpectationsWithTimeout(3.0) } }
1. ϞοΫαʔόʔͷىಈ 2. interactionͷొ 3. ϦΫΤετΛૹΔ 4. Ϩεϙϯεͷݕࠪ 5. PactϑΝΠϧͷੜ
func testSearchRecipe() { let manager = RecipeManager(baseURL: session.baseURL) let expected
= [ "recipes": [ ["name": "େτϩ", "description": "ඒຯ͍"], ["name": "͏ʹ", "description": “ඒຯ͍"], ] ] // Interactionͷొ let expectation = expectationWithDescription("recipes are found") session.given("some recipes exist") // Provider State .uponReceiving("a request for recipes") .with(method: .GET, path: "/recipes", query: [ "keyword": "ण࢘", ]) .willRespondWith(status: 200, body: expected)
func testSearchRecipe() { let manager = RecipeManager(baseURL: session.baseURL) let expected
= [ "recipes": [ ["name": "େτϩ", "description": "ඒຯ͍"], ["name": "͏ʹ", "description": “ඒຯ͍"], ] ] // Interactionͷొ let expectation = expectationWithDescription("recipes are found") session.given("some recipes exist") // Provider State .uponReceiving("a request for recipes") .with(method: .GET, path: "/recipes", query: [ "keyword": "ण࢘", ]) .willRespondWith(status: 200, body: expected)
func testSearchRecipe() { let manager = RecipeManager(baseURL: session.baseURL) let expected
= [ "recipes": [ ["name": "େτϩ", "description": "ඒຯ͍"], ["name": "͏ʹ", "description": “ඒຯ͍"], ] ] // Interactionͷొ let expectation = expectationWithDescription("recipes are found") session.given("some recipes exist") // Provider State .uponReceiving("a request for recipes") .with(method: .GET, path: "/recipes", query: [ "keyword": "ण࢘", ]) .willRespondWith(status: 200, body: expected)
1. ϞοΫαʔόʔͷىಈ 2. interactionͷొ 3. ϦΫΤετΛૹΔ 4. Ϩεϙϯεͷݕࠪ 5. PactϑΝΠϧͷੜ
session.run(completionBlock: { isValid in // completeTest࣮ߦޙʹݺΕΔ XCTAssertTrue(isValid) expectation.fulfill() }) {
completeTest in // APIϦΫΤετΛૹΔ manager.fetchRecipes(from: ”ण࢘") { (recipes, error) in XCTAssertEqual(recipes.count, 2) XCTAssertNil(error) XCTAssertEqual(recipes[0].name, "େτϩ") XCTAssertEqual(recipes[0].description, "ඒຯ͍") XCTAssertEqual(recipes[1].name, "͏ʹ") XCTAssertEqual(recipes[1].description, "ඒຯ͍") completeTest() // Mock ServerʹϨεϙϯε͕ਖ਼͍͜͠ͱΛ௨ } }) waitForExpectationsWithTimeout(3.0) }
session.run(completionBlock: { isValid in // completeTest࣮ߦޙʹݺΕΔ XCTAssertTrue(isValid) expectation.fulfill() }) {
completeTest in // APIϦΫΤετΛૹΔ manager.fetchRecipes(from: ”ण࢘") { (recipes, error) in XCTAssertEqual(recipes.count, 2) XCTAssertNil(error) XCTAssertEqual(recipes[0].name, "େτϩ") XCTAssertEqual(recipes[0].description, "ඒຯ͍") XCTAssertEqual(recipes[1].name, "͏ʹ") XCTAssertEqual(recipes[1].description, "ඒຯ͍") completeTest() // Mock ServerʹϨεϙϯε͕ਖ਼͍͜͠ͱΛ௨ } }) waitForExpectationsWithTimeout(3.0) }
session.run(completionBlock: { isValid in // completeTest࣮ߦޙʹݺΕΔ XCTAssertTrue(isValid) expectation.fulfill() }) {
completeTest in // APIϦΫΤετΛૹΔ manager.fetchRecipes(from: ”ण࢘") { (recipes, error) in XCTAssertEqual(recipes.count, 2) XCTAssertNil(error) XCTAssertEqual(recipes[0].name, "େτϩ") XCTAssertEqual(recipes[0].description, "ඒຯ͍") XCTAssertEqual(recipes[1].name, "͏ʹ") XCTAssertEqual(recipes[1].description, "ඒຯ͍") completeTest() // Mock ServerʹϨεϙϯε͕ਖ਼͍͜͠ͱΛ௨ } }) waitForExpectationsWithTimeout(3.0) }
import Foundation import XCTest import Phakchi class RecipeSearchPact: XCTest {
var session: Session! override func setUp() { super.setUp() let expectation = expectationWithDescription("Set up Pact environment") // ϞοΫαʔόʔͷॳظԽ controlServer.startSession(withConsumerName: "Cookpad", providerName: "APIServer") { newSession in if let newSession = newSession { self.session = newSession expectation.fulfill() } } waitForExpectationsWithTimeout(3.0) } func testSearchRecipe() { let manager = RecipeManager(baseURL: session.baseURL) let expected = [ "recipes": [ ["name": "େτϩ", "description": "ඒຯ͍"], ["name": "͏ʹ", "description": "ඒຯ͍"], ] ] // Interactionͷొ let expectation = expectationWithDescription("recipes are found") session.given("some recipes exist") // Provider State .uponReceiving("a request for recipes") .with(method: .GET, path: "/recipes", query: [ "keyword": "ण࢘", ]) .willRespondWith(status: 200, body: expected) session.run(completionBlock: { isValid in // completeTest࣮ߦޙʹݺΕΔ XCTAssertTrue(isValid) expectation.fulfill() }) { completeTest in // APIϦΫΤετΛૹΔ manager.fetchRecipesFromKeyword("ण࢘") { (recipes, error) in XCTAssertEqual(recipes.count, 2) XCTAssertNil(error) XCTAssertEqual(recipes[0].name, "େτϩ") XCTAssertEqual(recipes[0].description, "ඒຯ͍") XCTAssertEqual(recipes[1].name, "͏ʹ") XCTAssertEqual(recipes[1].description, "ඒຯ͍") completeTest() // Mock ServerʹϨεϙϯε͕ਖ਼͍͜͠ͱΛ௨ } }) waitForExpectationsWithTimeout(3.0) } }
$POTVNFS ϞόΠϧΞϓϦ 1SPWJEFS "1* #SPLFS HTTP Request HTTP Response Pact
file Pact fileʹهड़ͨ͠ϦΫΤετΛ͛ͯݕূ͢Δ
None
ಋೖͯ͠Έͯ
ಋೖͯ͠Έͯ • ҰԠAPIͷมߋʹؾ͚ͮΔΑ͏ʹͳͬͨ • ·ͩ·ͩςετέʔε͕গͳ͍ͷͰະ
• CIͷӡ༻ • ϚʔδλΠϛϯάͷ • ڊେͳϨεϙϯεͷରԠ͕͍͠
Any Questions?