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
giginet
0
1.7k
giginet
4
2.1k
giginet
3
6k
giginet
0
910
giginet
1
6.4k
giginet
13
4.7k
giginet
0
89
giginet
0
180
giginet
2
190
Other Decks in Technology
See All in Technology
nihonbuson
2
1.9k
clustervr
0
210
kawaguti
0
120
con_mame
4
2k
hamadakoji
1
1.1k
minamizaki
0
710
masashible
0
110
yosuke_furukawa
PRO
47
16k
ocise
1
1.3k
papix
0
160
ishiayaya
PRO
0
350
clustervr
0
160
Featured
See All Featured
rasmusluckow
318
18k
thoeni
4
550
akmur
252
19k
jnunemaker
PRO
40
4.6k
jrom
114
7.1k
mongodb
23
3.8k
scottboms
251
11k
brettharned
93
3k
geoffreycrofte
19
800
michaelherold
225
8.5k
bryan
30
3.3k
reverentgeek
27
1.9k
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?