Upgrade to Pro — share decks privately, control downloads, hide ads and more …

HTTPモックライブラリ「Mockingjay」を使ってみた話/swift-mockingjay

 HTTPモックライブラリ「Mockingjay」を使ってみた話/swift-mockingjay

iOS Test Night #8 での発表資料です。
https://testnight.connpass.com/event/90959/

Yusuke Hosonuma

July 24, 2018
Tweet

More Decks by Yusuke Hosonuma

Other Decks in Programming

Transcript

  1. Copyright (C) DeNA Co.,Ltd. All Rights Reserved. iOS Test Night

    #8 2018/07/24 (Tue) 
 ࡉপ ༞հ@DeNA )5514UVCϥΠϒϥϦ ʮ.PDLJOHKBZʯΛ࢖ͬͯΈͨ࿩ 1
  2. ˙:VTVLF)PTPOVNBʢ!UPCJʣ • ॴଐ ⁃ %F/"ɹ48&5άϧʔϓɹςετج൫νʔϜ • 'BWPSJUF ⁃  •

    ۙگ ⁃ ,VCFSOFUFT(,&ָ͕͍͠ʂ ⁃ J04%$ͰϨΪϡϥʔτʔΫʢ෼ʣʹ࠾୒ ࣗݾ঺հ Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 2
  3. ΞδΣϯμ 5 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. •

    )5514UVCͱ͸ʁ • ࠓճͷར༻έʔε • .PDLJOHKBZͱ͸ʁ • ޻෉ͨ͠ͱ͜Ζ • ·ͱΊ
  4. HTTP Stub ͱ͸ʁ 7 Copyright (C) DeNA Co.,Ltd. All Rights

    Reserved. • )551ϦΫΤετ௨৴ʹର͢Δ4UVCʢِ෺ʣ • ϦΫΤετʹରͯ͠ɺ೚ҙͷϨεϙϯεΛฦ͢ • ֎෦αʔόʹґଘ͠ͳ͍ςετ͕ॻ͚Δ
  5. HTTP௨৴ 9 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. ΞϓϦ

    αʔό (&5BQJBSUJDMF 0, ʲ՝୊ʳ Τϥʔ͕ฦ٫͞Εͨ৔߹ͷ ςετ͸Ͳ͏΍Δʁ
  6. HTTP௨৴ϨϕϧͷςετΛͲ͏͢Δʁ 10 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. •

    ΍Βͳ͍ • ௨৴༻.PEFMΫϥεͷ4UVCͰΧόʔ • &&ςετͰΧόʔ • ΍Δ • αʔόଆͷઃఆΛҰ࣌తʹมߋ • )5514UVC
  7. HTTP௨৴ϨϕϧͷςετΛͲ͏͢Δʁ 11 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. •

    ΍Βͳ͍ • ௨৴༻.PEFMΫϥεͷ4UVCͰΧόʔ • &&ςετͰΧόʔ • ΍Δ • αʔόଆͷઃఆΛҰ࣌తʹมߋ • )5514UVC ࠓճͷબ୒
  8. HTTP Stub 12 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    ΞϓϦ αʔό (&5BQJBSUJDMF /05@'06/% )5514UVC αʔόͱͷؒʹೖͬͯɺ ೚ҙͷϨεϙϯεΛฦ͢ʂ
  9. ࠓճͷར༻έʔε 14 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. Client

    αʔό 4XJGU1. αʔόͱԿ౓΋௨৴͢Δ ઃఆ஋ʹج͍ͮͯ$-*Λ࣮ߦͯ͠ɺ ҙਤͨ͠௨৴͕ߦΘΕΔ͔Λݕূ͍ͨ͠ʜ $-*πʔϧ
  10. )5514UVC ࠓճͷར༻έʔε 15 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    Client αʔό 4XJGU1. )5514UVCϥΠϒϥϦΛ࢖ͬͯɺ ΠϯςάϨʔγϣϯʢ݁߹ʣςετ .PDLJOHKBZΛ࢖ͬͯΈͨ
  11. Mockingjayͱ͸ʁ 17 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. •

    )5514UVCϥΠϒϥϦ • 4XJGU • ϦΫΤετఆٛʢ.BUDIFSʣ • Ϩεϙϯεੜ੒ʢ#VJMEFSʣ • 63-4FTTJPO • $POpHVSBUJPOΛࠩ͠ସ͑ • 63-4FTTJPOҎ֎ͷ௨৴ʹ͸ར༻Ͱ͖ͳ͍ IUUQTHJUIVCDPNLZMFG.PDLJOHKBZ .FUIPE4XJ[[MJOH
  12. ModelΫϥε 18 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. let

    url = "http://example.com/api" struct ProposalRepository { func all(_ handler: @escaping ([ProposalElement]) -> Void) { Alamofire.request(url).responseProposal { response in if let proposals = response.result.value { handler(proposals) } } } }
  13. ModelΫϥε 19 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. let

    url = "http://example.com/api" struct ProposalRepository { func all(_ handler: @escaping ([ProposalElement]) -> Void) { Alamofire.request(url).responseProposal { response in if let proposals = response.result.value { handler(proposals) } } } } BQJ
  14. ModelΫϥε 20 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. let

    url = "http://example.com/api" struct ProposalRepository { func all(_ handler: @escaping ([ProposalElement]) -> Void) { Alamofire.request(url).responseProposal { response in if let proposals = response.result.value { handler(proposals) } } } } "MBNPpSF
  15. ModelΫϥε 21 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. let

    url = "http://example.com/api" struct ProposalRepository { func all(_ handler: @escaping ([ProposalElement]) -> Void) { Alamofire.request(url).responseProposal { response in if let proposals = response.result.value { handler(proposals) } } } } $BMMCBDL
  16. ModelΫϥε 22 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. let

    url = "http://example.com/api" struct ProposalRepository { func all(_ handler: @escaping ([ProposalElement]) -> Void) { Alamofire.request(url).responseProposal { response in if let proposals = response.result.value { handler(proposals) } } } } $BMMCBDL ͪͳΈʹRVJDLUZQFJPͰίʔυੜ੒
  17. GET /api ʹରͯ͠ɺJSONΛฦ͢ 23 Copyright (C) DeNA Co.,Ltd. All Rights

    Reserved. let data: [[String: String]] = [ [ "title": "λΠτϧ1", "user": "Ϣʔβ໊", ], [ "title": "λΠτϧ2", "user": "Ϣʔβ໊", ], ] stub(http(.get, uri: "/api"), json(data))
  18. GET /api ʹରͯ͠ɺJSONΛฦ͢ 24 Copyright (C) DeNA Co.,Ltd. All Rights

    Reserved. let data: [[String: String]] = [ [ "title": "λΠτϧ1", "user": "Ϣʔβ໊", ], [ "title": "λΠτϧ2", "user": "Ϣʔβ໊", ], ] stub(http(.get, uri: "/api"), json(data)) Ϩεϙϯεσʔλ
  19. GET /api ʹରͯ͠ɺJSONΛฦ͢ 25 Copyright (C) DeNA Co.,Ltd. All Rights

    Reserved. let data: [[String: String]] = [ [ "title": "λΠτϧ1", "user": "Ϣʔβ໊", ], [ "title": "λΠτϧ2", "user": "Ϣʔβ໊", ], ] stub(http(.get, uri: "/api"), json(data)) ॲཧ͢ΔύεΛఆٛ
  20. GET /api ʹରͯ͠ɺJSONΛฦ͢ 26 Copyright (C) DeNA Co.,Ltd. All Rights

    Reserved. let data: [[String: String]] = [ [ "title": "λΠτϧ1", "user": "Ϣʔβ໊", ], [ "title": "λΠτϧ2", "user": "Ϣʔβ໊", ], ] stub(http(.get, uri: "/api"), json(data)) Ϩεϙϯεఆٛ
  21. ࣮ࡍͷςετίʔυ 27 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. let

    data: [[String: String]] = [ [ "title": "λΠτϧ1", "user": "Ϣʔβ໊" ], [ "title": "λΠτϧ2", "user": "Ϣʔβ໊" ], ] stub(http(.get, uri: "/api"), json(data)) let exp = expectation(description: "") repository.all { proposals in XCTAssertEqual(2, proposals.count) XCTAssertEqual("λΠτϧ1", proposals.first?.title) XCTAssertEqual("Ϣʔβ໊", proposals.first?.user) exp.fulfill() } wait(for: [exp], timeout: 3)
  22. ࣮ࡍͷςετίʔυ 28 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. let

    data: [[String: String]] = [ [ "title": "λΠτϧ1", "user": "Ϣʔβ໊" ], [ "title": "λΠτϧ2", "user": "Ϣʔβ໊" ], ] stub(http(.get, uri: "/api"), json(data)) let exp = expectation(description: "") repository.all { proposals in XCTAssertEqual(2, proposals.count) XCTAssertEqual("λΠτϧ1", proposals.first?.title) XCTAssertEqual("Ϣʔβ໊", proposals.first?.user) exp.fulfill() } wait(for: [exp], timeout: 3) )5514UVCઃఆ
  23. ࣮ࡍͷςετίʔυ 29 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. let

    data: [[String: String]] = [ [ "title": "λΠτϧ1", "user": "Ϣʔβ໊" ], [ "title": "λΠτϧ2", "user": "Ϣʔβ໊" ], ] stub(http(.get, uri: "/api"), json(data)) let exp = expectation(description: "") repository.all { proposals in XCTAssertEqual(2, proposals.count) XCTAssertEqual("λΠτϧ1", proposals.first?.title) XCTAssertEqual("Ϣʔβ໊", proposals.first?.user) exp.fulfill() } wait(for: [exp], timeout: 3) .PEFMͷςετ
  24. ࣮ࡍͷςετίʔυ 30 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. let

    data: [[String: String]] = [ [ "title": "λΠτϧ1", "user": "Ϣʔβ໊" ], [ "title": "λΠτϧ2", "user": "Ϣʔβ໊" ], ] stub(http(.get, uri: "/api"), json(data)) let exp = expectation(description: "") repository.all { proposals in XCTAssertEqual(2, proposals.count) XCTAssertEqual("λΠτϧ1", proposals.first?.title) XCTAssertEqual("Ϣʔβ໊", proposals.first?.user) exp.fulfill() } wait(for: [exp], timeout: 3) ඇಉظॲཧΛ଴ͭ
  25. Mockingjay API 33 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    stub(http(.get, uri: "/api")) { request in let data: [[String: String]] = [ [ "title": "λΠτϧ1", "user": "Ϣʔβ໊" ], [ "title": "λΠτϧ2", "user": "Ϣʔβ໊" ], ] return jsonBody(data)(request) }
  26. Mockingjay API 34 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    stub(http(.get, uri: "/api")) { request in let data: [[String: String]] = [ [ "title": "λΠτϧ1", "user": "Ϣʔβ໊" ], [ "title": "λΠτϧ2", "user": "Ϣʔβ໊" ], ] return jsonBody(data)(request) } ͕ଟͯ͘গ͠ಡΈͮΒ͍ʜ • ϊΠζײ͕͋Δ • .BUDIFS"1*͸ίʔυิ׬ͮ͠Β͍
  27. ϥούʔAPI Λॻ͍ͯΈΔ 35 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    func stubHttp(_ method: HTTPMethod, _ path: String, _ builder: @escaping Builder) { stub(http(method, uri: path)) { request in builder(request) } } ຊ౰ʹϥοϓ͚ͨͩ͠
  28. Before / After 36 Copyright (C) DeNA Co.,Ltd. All Rights

    Reserved. // // Before: // stub(http(.get, uri: "/api") { } // // After // stubHttp(.get, "/api") { } • ࢹೝੑ͕Α͘ͳͬͨʂ • ֮͑Δ͜ͱ΋গͳ͘ɺิ׬΋͠΍͍͢ʂ
  29. ૺ۰ͨ͠ࣄ৅ 38 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. 

    TUVCΛઃఆ  ςετΛॻ͘  ੒ޭͯ͠ظ଴Ͳ͓Γಈ͍ͯΔͱࢥͬͨ  ࣮ࡍʹ͸63-ͷύε͕ҟͳ͓ͬͯΓ
 ݺ͹Ε͍ͯͳ͔ͬͨPS[ ͜͏͍ͬͨϛεΛແ͍ͨ͘͠ɾɾɾ
  30. Stub ݺͼग़͠Λอূ͢Δ 39 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    let exp = expectation(description: “/api") stubHttp(.get, "/api") { request in exp.fulfill() let data: [[String: String]] = [] return jsonBody(data)(request) } wait(for: [exp], timeout: 3)
  31. Stub ݺͼग़͠Λอূ͢Δ 40 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    let exp = expectation(description: “/api") stubHttp(.get, "/api") { request in exp.fulfill() let data: [[String: String]] = [] return jsonBody(data)(request) } wait(for: [exp], timeout: 3) 9$5FTU&YQFDUBUJPOΛੜ੒
  32. Stub ݺͼग़͠Λอূ͢Δ 41 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    let exp = expectation(description: “/api") stubHttp(.get, "/api") { request in exp.fulfill() let data: [[String: String]] = [] return jsonBody(data)(request) } wait(for: [exp], timeout: 3) 4UVC͕ݺͼग़͞ΕͨΒGVMMpMM
  33. Stub ݺͼग़͠Λอূ͢Δ 42 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    let exp = expectation(description: “/api") stubHttp(.get, "/api") { request in exp.fulfill() let data: [[String: String]] = [] return jsonBody(data)(request) } wait(for: [exp], timeout: 3) GVMMpMM͞Εͯͳ͚Ε͹ λΠϜΞ΢τͰࣦഊ
  34. Stub ݺͼग़͠Λอূ͢Δ 43 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    let exp = expectation(description: “/api") stubHttp(.get, "/api") { request in exp.fulfill() let data: [[String: String]] = [] return jsonBody(data)(request) } wait(for: [exp], timeout: 3) • TUVC͕ݺͼग़͞Εͳ͍৔߹͸Τϥʔʹʂ • Ͱ΋ɺ৑௕ͳίʔυ͕ଟ͍ʁˠ͜Ε͸࣍Ͱʂ
  35. ݱঢ়ͷ՝୊ 45 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. •

    TUVC͕ݺͼग़͞ΕΔ͜ͱΛอূͰ͖ͨ • ຖճ͜ΕΛॻ͘ͷ͸໘౗ • ຊ࣭తͰͳ͍ίʔυ͕ଟ͘ͳΔ໰୊΋
  36. ݱঢ়ͷ՝୊ 46 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. •

    TUVC͕ݺͼग़͞ΕΔ͜ͱΛอূͰ͖ͨ • ຖճ͜ΕΛॻ͘ͷ͸໘౗ • ຊ࣭తͰͳ͍ίʔυ͕ଟ͘ͳΔ໰୊΋ ΋ͬͱָΛ͍ͨ͠ʂ
  37. ࠷ऴతͳίʔυ 47 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. expectCall(

    stubHttp(.get, “/api/v1“) { request in ... } ) expectCall( stubHttp(.get, “/api/v2“) { request in ... } ) waitCallExpectation()
  38. stubHttp ϝιουͷ֦ு 48 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    func stubHttp(_ method: HTTPMethod, _ path: String, _ builder: @escaping Builder) -> XCTestExpectation { let exp = expectation(description: "request \(method): \(path)") stub(http(method, uri: path)) { request in exp.fulfill() return builder(request) } return exp }
  39. stubHttp ϝιουͷ֦ு 49 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    func stubHttp(_ method: HTTPMethod, _ path: String, _ builder: @escaping Builder) -> XCTestExpectation { let exp = expectation(description: "request \(method): \(path)") stub(http(method, uri: path)) { request in exp.fulfill() return builder(request) } return exp } ಺෦Ͱ&YQFDUBUJPOΛੜ੒
  40. stubHttp ϝιουͷ֦ு 50 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    func stubHttp(_ method: HTTPMethod, _ path: String, _ builder: @escaping Builder) -> XCTestExpectation { let exp = expectation(description: "request \(method): \(path)") stub(http(method, uri: path)) { request in exp.fulfill() return builder(request) } return exp } TUVC͕ݺͼग़͞ΕͨΒGVMMpMM
  41. stubHttp ϝιουͷ֦ு 51 Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

    func stubHttp(_ method: HTTPMethod, _ path: String, _ builder: @escaping Builder) -> XCTestExpectation { let exp = expectation(description: "request \(method): \(path)") stub(http(method, uri: path)) { request in exp.fulfill() return builder(request) } return exp } &YQFDUBUJPOΛSFUVSOͰฦ͢
  42. ਌ͷςετΫϥεΛ࡞੒ 52 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. class

    HttpTestCase: XCTestCase { private var stubHttpCallExpectations: [XCTestExpectation]! } extension HttpTestCase { func expectCall(_ exp: XCTestExpectation) { stubHttpCallExpectations.append(exp) } func waitCallExpectation() { wait(for: stubHttpCallExpectations, timeout: 3) } }
  43. ਌ͷςετΫϥεΛ࡞੒ 53 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. class

    HttpTestCase: XCTestCase { private var stubHttpCallExpectations: [XCTestExpectation]! } extension HttpTestCase { func expectCall(_ exp: XCTestExpectation) { stubHttpCallExpectations.append(exp) } func waitCallExpectation() { wait(for: stubHttpCallExpectations, timeout: 3) } } 9$5FTU&YQFDUBUJPOͷ഑ྻΛ༻ҙ
  44. ਌ͷςετΫϥεΛ࡞੒ 54 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. class

    HttpTestCase: XCTestCase { private var stubHttpCallExpectations: [XCTestExpectation]! } extension HttpTestCase { func expectCall(_ exp: XCTestExpectation) { stubHttpCallExpectations.append(exp) } func waitCallExpectation() { wait(for: stubHttpCallExpectations, timeout: 3) } } Ҿ਺ʹ౉͞Εͨ&YQFDUBUJPOΛ௥Ճ
  45. ਌ͷςετΫϥεΛ࡞੒ 55 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. class

    HttpTestCase: XCTestCase { private var stubHttpCallExpectations: [XCTestExpectation]! } extension HttpTestCase { func expectCall(_ exp: XCTestExpectation) { stubHttpCallExpectations.append(exp) } func waitCallExpectation() { wait(for: stubHttpCallExpectations, timeout: 3) } } ௥Ճ͞Εͨ&YQFDUBUJPOΛXBJU
  46. શମ 56 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. class

    HttpTestCase: XCTestCase { private var stubHttpCallExpectations: [XCTestExpectation]! } extension HttpTestCase { func expectCall(_ exp: XCTestExpectation) { stubHttpCallExpectations.append(exp) } func waitCallExpectation() { wait(for: stubHttpCallExpectations, timeout: 3) } } // ςετίʔυ expectCall( stubHttp(.get, “/api/v1“) { request in ... } ) waitCallExpectation()
  47. શମ 57 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. class

    HttpTestCase: XCTestCase { private var stubHttpCallExpectations: [XCTestExpectation]! } extension HttpTestCase { func expectCall(_ exp: XCTestExpectation) { stubHttpCallExpectations.append(exp) } func waitCallExpectation() { wait(for: stubHttpCallExpectations, timeout: 3) } } // ςετίʔυ expectCall( stubHttp(.get, “/api/v1“) { request in ... } ) waitCallExpectation() ฦ٫͞Εͨ&YQFDUBUJPO͕Ҿ਺ʹ౉Δ
  48. શମ 58 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. class

    HttpTestCase: XCTestCase { private var stubHttpCallExpectations: [XCTestExpectation]! } extension HttpTestCase { func expectCall(_ exp: XCTestExpectation) { stubHttpCallExpectations.append(exp) } func waitCallExpectation() { wait(for: stubHttpCallExpectations, timeout: 3) } } // ςετίʔυ expectCall( stubHttp(.get, “/api/v1“) { request in ... } ) waitCallExpectation() ௥Ճ͞Εͨ&YQFDUBUJPOΛXBJU
  49. શମ 59 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. class

    HttpTestCase: XCTestCase { private var stubHttpCallExpectations: [XCTestExpectation]! } extension HttpTestCase { func expectCall(_ exp: XCTestExpectation) { stubHttpCallExpectations.append(exp) } func waitCallExpectation() { wait(for: stubHttpCallExpectations, timeout: 3) } } // ςετίʔυ expectCall( stubHttp(.get, “/api/v1“) { request in ... } ) waitCallExpectation()
  50. ·ͱΊ 61 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. •

    .PDLJOHKBZ͸4XJGUͷ)5514UVCϥΠϒϥϦ • )551௨৴ͷςετʹ࢖͑Δ • "1*ϥούʔΛॻ͍ͯྑ͔ͬͨ • ৑௕ίʔυ͕গͳ͘ɺҙਤ͕໌֬ʹ • ίʔυิ׬΋շద
  51. ·ͱΊ 62 Copyright (C) DeNA Co.,Ltd. All Rights Reserved. •

    .PDLJOHKBZ͸4XJGUͷ)5514UVCϥΠϒϥϦ • )551௨৴ͷςετʹ࢖͑Δ • "1*ϥούʔΛॻ͍ͯྑ͔ͬͨ • ৑௕ίʔυ͕গͳ͘ɺҙਤ͕໌֬ʹ • ίʔυิ׬΋շద )5514UVC͸ ͓֮͑ͯ͘ͱ෢ثͱͯ͠໾ཱ͔ͭ΋ʂʁ
  52. iOSDC ͷ CfP ҰཡΛݟΕΔαʔϏεΛ ϓϥΠϕʔτͰ࡞Γ·ͨ͠ʂ 64 Copyright (C) DeNA Co.,Ltd.

    All Rights Reserved. h(p://iosdc-cfps.penginmura.tech/ ࠓޙ΋ػೳ௥ՃΛ༧ఆ