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/

Avatar for Yusuke Hosonuma

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/ ࠓޙ΋ػೳ௥ՃΛ༧ఆ