CDC Testing on iOS

011714704c4a925e542d426d4cdaa4e3?s=47 giginet
September 13, 2016

CDC Testing on iOS

We integrated Pact for iOS applications.

http://cookpad-tech-kitchen.connpass.com/event/37908/

011714704c4a925e542d426d4cdaa4e3?s=128

giginet

September 13, 2016
Tweet

Transcript

  1. CDC Testing on iOS Cookpad Tech Kitchen Vol2 @giginet

  2. ୭ʁ • @giginet • ٕज़෦ϞόΠϧج൫άϧʔϓ • ৽ଔ2೥໨

  3. Agenda • CDC Testingͱ͸ • Pact • Phakchi • ஌ݟ

  4. ՝୊ • APIͷมߋʹΑΔαʔϏεͷഁյͷݕ஌ • αʔόʔɺΫϥΠΞϯτ։ൃऀؒͰͷ࢓༷ڞ ༗ͷඞཁੑ

  5. CDC Testing

  6. CDC Testing • Consumer-Driven Contract Testing • APIͱΫϥΠΞϯτؒͷ੔߹ੑΛςετ͢Δ ͨΊͷςετख๏

  7. ༻ޠ • Provider • APIΛఏڙ͢Δଆ = APIαʔόʔ • Consumer •

    APIΛར༻͢Δଆ = iOSΞϓϦ • Contract • APIͷ࢓༷
  8. Pact • CDC TestingͷͨΊͷϑϨʔϜϫʔΫ • http://docs.pact.io/ • https://github.com/realestate-com-au/pact • ݩʑ͸αʔόʔؒ࿈ܞΛ૝ఆ

  9. ࣮ફ Pact:ϚΠΫϩαʔϏε࣌୅ͷςετπʔϧ http://techlife.cookpad.com/entry/ 2016/06/28/164247

  10. $POTVNFS ϞόΠϧΞϓϦ 1SPWJEFS "1* #SPLFS HTTP Request HTTP Response Pact

    file ϦΫΤετͱϨεϙϯεΛBrokerʹه࿥͢Δ
  11. Contract(ྫ) • GET /recipes?keyword=ण࢘ { "recipes":[ { "name":"େτϩ", "description":"ඒຯ͍" },

    { "name":"͏ʹ", "description":"ඒຯ͍" } ] }
  12. $POTVNFS ϞόΠϧΞϓϦ 1SPWJEFS "1* #SPLFS HTTP Request HTTP Response Pact

    file Pact fileʹهड़ͨ͠ϦΫΤετΛ౤͛ͯݕূ͢Δ
  13. $POTVNFS ϞόΠϧΞϓϦ 1SPWJEFS "1* #SPLFS HTTP Request HTTP Response Pact

    file
  14. 6OJU5FTU 1BDU.PDL4FSWFS Ϣχοτςετͷىಈ࣌ʹϞο ΫαʔόʔΛ্ཱͪ͛Δ ىಈ

  15. 6OJU5FTU 1BDU.PDL4FSWFS ϞοΫαʔόʔʹϦΫΤετͱ Ϩεϙϯεͷ૊ JOUFSBDUJPO Λొ ࿥͢Δ ϦΫΤετͱ ظ଴͢ΔϨεϙϯεΛ ొ࿥

  16. 6OJU5FTU 1BDU.PDL4FSWFS Ϣχοτςετ͔ΒϞοΫαʔ όʔʹରͯ͠ϦΫΤετΛૹΓɺର ʹͳΔϨεϙϯεΛฦ͢ ϦΫΤετ Ϩεϙϯε

  17. 6OJU5FTU 1BDU.PDL4FSWFS ड͚औͬͨϨεϙϯεΛΫϥΠ ΞϯτଆͰݕࠪ͠ɺਖ਼͚͠Ε͹Ϟο Ϋαʔόʔʹ఻͑Δ 1BDUϑΝΠϧ͕ੜ੒ ऴྃΛ௨஌ ੜ੒ Pact file

  18. #SPLFS ϞοΫαʔόʔ͕1BDUϑΝΠϧ Λੜ੒͢ΔͷͰ#SPLFSʹΞοϓ ϩʔυ͢Δ Ξοϓϩʔυ Pact file

  19. cookpad/Phakchi https://github.com/cookpad/Phakchi

  20. Phakchi • PactͷςετέʔεΛָʹهड़͢ΔϥΠϒϥϦ • Swift੡ • XCTest͔Βར༻ • cf :

    https://github.com/DiUS/pact-consumer- swift
  21. 1. ϞοΫαʔόʔͷىಈ 2. interactionͷొ࿥ 3. ϦΫΤετΛૹΔ 4. Ϩεϙϯεͷݕࠪ 5. PactϑΝΠϧͷੜ੒

  22. 1. ϞοΫαʔόʔͷىಈ 2. interactionͷొ࿥ 3. ϦΫΤετΛૹΔ 4. Ϩεϙϯεͷݕࠪ 5. PactϑΝΠϧͷੜ੒

  23. None
  24. 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) } }
  25. 1. ϞοΫαʔόʔͷىಈ 2. interactionͷొ࿥ 3. ϦΫΤετΛૹΔ 4. Ϩεϙϯεͷݕࠪ 5. PactϑΝΠϧͷੜ੒

  26. 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)
  27. 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)
  28. 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)
  29. 1. ϞοΫαʔόʔͷىಈ 2. interactionͷొ࿥ 3. ϦΫΤετΛૹΔ 4. Ϩεϙϯεͷݕࠪ 5. PactϑΝΠϧͷੜ੒

  30. 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) }
  31. 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) }
  32. 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) }
  33. 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) } }
  34. $POTVNFS ϞόΠϧΞϓϦ 1SPWJEFS "1* #SPLFS HTTP Request HTTP Response Pact

    file Pact fileʹهड़ͨ͠ϦΫΤετΛ౤͛ͯݕূ͢Δ
  35. None
  36. ಋೖͯ͠Έͯ

  37. ಋೖͯ͠Έͯ • ҰԠ͸APIͷมߋʹؾ͚ͮΔΑ͏ʹͳͬͨ • ·ͩ·ͩςετέʔε͕গͳ͍ͷͰະ஌਺

  38. ໰୊఺ • CIͷӡ༻ • ϚʔδλΠϛϯάͷ໰୊ • ڊେͳϨεϙϯε΁ͷରԠ͕೉͍͠

  39. Any Questions?