This document explain about asynchronous testing in XCTest
Asyncronous Testingin XCTest
View Slide
XCTestʹ͓͚ΔඇಉظॲཧͷςετඇಉظͷςετͷͨΊʹXCTestExpectationͱXCTWaiter༻ҙ͞Ε͍ͯ·͢ɻXCTestExpectationͷfulfillͷ࣮ߦΛ࣋ͬͯඇಉظॲཧͷྃͱ͠waitͰࢦఆͨ͠expectationͷྃʹػ͢Δͷ͕جຊతͳྲྀΕͰ͢ɻ• XCTestExpectation• XCTWaiter
XCTestExpectationfunc test_fetchPilot() {// 1.XCTestExpectationΛ࡞let expectation = XCTestExpectation(description: "pilot")PilotApiClient.fetchPilot(of: "new_type") { (result) inswitch result {case .success(let user):XCTAssert(user.name == "REI")case .failure(_):break}// 2.ඇಉظॲཧͷ࣮ߦϒϩοΫͰfulfillΛ࣮ߦexpectation.fulfill()}// 3.waitؔʹexpectationΛࢦఆwait(for: [expectation], timeout: 2) //<- 1ඵະຬͰςετࣦഊ}
XCTestExpectation• expectedFulfillmentCount• isInverted
XCTestExpectationexpectedFulfillmentCountʹճΛࢦఆ͢ΔͱࢦఆճҎԼλΠϜΞτͰςετࣦഊͱ͞Ε·͢ɻ/// RadarAPIClient͔Β·ͣ1ඵޙʹɺͦͯͦ͠Ε͔Β5ඵޙʹίʔϧόοΫ͕ฦ͖ͬͯ·͢ɻ/// ෳճίʔϧόοΫ͕࣮ߦ͞ΕΔΛߟྀͯ͠Έ·͠ΐ͏!let expectation = XCTestExpectation(description: "radar")expectation.expectedFulfillmentCount = 2let here = MyLocation(latitude: 35.664584, longitude: 139.730852)RadarApiClient.observeEnemy(from: here) { [expectation] (result) inswitch result {case .success(let enemies):XCTAssert(enemies.count > 0)case .failure(_):break}expectation.fulfill()}wait(for: [expectation], timeout: 10)
XCTestExpectationisInvertedΛࢦఆ͢ΔͱfulfillΛ"࣮ߦ͞Εͳ͍߹"ʹςετ͕ޭ͠·͢ɻlet expectation = XCTestExpectation(description: "inverted")expectation.isInverted = true // fulfill͕ى͖ͳ͔ͬͨ߹ʹςετޭͱ͢Δwait(for: [expectation], timeout: 1)
XCTWaiter
XCTWaiter͔͜͜ΒXCTWaiterͷΛ͠·͢ɻXCTWaiterwaitϝιουͳͲͷΑ͏ͳඇಉظॲཧͷྃΛͭͨΊͷॲཧʹ͍ͭͯͷػೳΛ୲͠·͢ɻྫ͑waitXCTWaiterDelegateͰఆٛ͞Ε͓ͯΓɺXCTestCase͕͜Εʹ४ڌ͍ͯ͠·͢ɻ
wait(or waitForExpectations)• ॲཧྃ࣌ʹಛఆͷॲཧΛ࣮ߦ͍ͨ͠߹• ඇಉظॲཧͷλΠϜΞτ͕ҟͳΔ߹• ඇಉظॲཧͷऴྃॱ͕݅ͱͳΔ߹(enforceOrder)
wait(or waitForExpectations)ॲཧྃ࣌ʹಛఆͷॲཧΛ࣮ߦ͍ͨ͠߹let expectation = self.expectation(description: "waitExpectations")DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {expectation.fulfill() //fulfillͳͩ͠ͱ.timeoutWhileWaitingʹͳΔ}// ίʔϧόοΫͷҾError?Ͱ͋Γɺਖ਼ৗ࣌λΠϜΞτ࣌XCTestError͕ฦ٫͞ΕΔwaitForExpectations(timeout: 1) { (error) inguard let error = error as? XCTestError else {return}switch error.code {case .failureWhileWaiting:print("!ࣦഊ࣌ͪ͜Β") //ਖ਼ίϨͷى͜͠ํ͕Θ͔ͬͯͳ͍breakcase .timeoutWhileWaiting:print("!λΠϜΞτͷ߹ͪ͜Β")}}
wait(or waitForExpectations)ඇಉظॲཧͷλΠϜΞτ͕ҟͳΔ߹ҰͭͷwaitͰॲཧͤͣʹෳͷwaitΛ͏ࣄͰλΠϜΞτͷ࣌ؒΛ͚Δ͜ͱ͕Ͱ͖Δɻlet expForPilot = XCTestExpectation(description: "pilot")fetchPilot(expForPilot)let expForColony = XCTestExpectation(description: "colony")fetchColony(expForColony)// λΠϜΞτͷ͕࣌ؒҧ͏ͷͰ͋ΕwaitؔΛෳʹ͚࣮ͯߦ͢Δࣄग़དྷΔɻwait(for: [expForPilot], timeout: 2)wait(for: [expForColony], timeout: 4)
wait(or waitForExpectations)ಛఆͷॱ൪Λظͯ͠ςετ͢Δ߹enforceOrderΛtrueʹ͢ΔࣄͰ࣮ߦྃॱग़ͳ͍߹ʹςετࣦഊͱ͢Δ͜ͱ͕Ͱ͖Δɻlet expForPilot = XCTestExpectation(description: "pilot")let expForColony = XCTestExpectation(description: "colony")// ྫ͑fetchColonyͷޙʹfetchPilot͠ͳ͚Ε͍͚ͳ͍ཁ͕݅͋ͬͨ߹fetchColony(expForColony) {fetchPilot(expForPilot)}wait(for: [expForColony, expForPilot],timeout: 10,enforceOrder: true) //<- enforceOrderΛࢦఆ͢ΔࣄͰexpectationͷॱং·ͰݟΔ͜ͱ͕Ͱ͖Δ
৭ʑͳExpectationୡ
৭ʑͳExpectationୡ• XCTKVOExpectation• XCTNSNotificationExpectation• XCTNSPredicateExpectation• XCTDarwinNotificationExpectation
XCTKVOExpectationKVO(Key Value Observe)Λར༻ͨ͠ExpectationKeyPathʹࢦఆͨ͠ϓϩύςΟͷࢹΛߦ͏ࣄͰར༻Մೳlet noobPilot = Pilot(id: "noob", name: "Arbeo", abilities: [])let battleField = BattleField(pilots: [noobPilot])let expectation = XCTKVOExpectation(keyPath: "mind", object: noobPilot)battleField.startBattle()wait(for: [expectation], timeout: 3)
XCTNSNotificationExpectationNSNotificationΛར༻ͨ͠Expectationࢦఆ໊ͨ͠લͷ௨Λ࣋ͬͯྃͱ͢Δࣄ͕Ͱ͖Δɻlet noobPilot = Pilot(id: "noob", name: "Arbeo", abilities: [])let battleField = BattleField(pilots: [noobPilot])let expectation = XCTNSNotificationExpectation(name: Notification.Name("ͬͯΔʂͬͯΔͧʂ"))battleField.startBattle()wait(for: [expectation], timeout: 3)
XCTNSPredicateExpectationNSPredicateΛར༻ͨ͠Expectation݅ʹ߹கΛॲཧྃͱ͢Δ͜ͱ͕Ͱ͖Δlet noobPilot = Pilot(id: "noob", name: "Arbeo", abilities: [])let battleField = BattleField(pilots: [noobPilot])let predicate = NSPredicate(format: "mind == 1")let expectation = XCTNSPredicateExpectation(predicate: predicate, object: noobPilot)battleField.startBattle()wait(for: [expectation], timeout: 3)
XCTDarwinNotificationExpectationDarwin NotificationΛར༻ͨ͠ExpectationNSNotificationͱಉ͘͡ࢦఆ໊ͨ͠લͷ௨Λ࣋ͬͯྃͱ͢Δࣄ͕Ͱ͖Δɻlet noobPilot = Pilot(id: "noob", name: "Arbeo", abilities: [])let battleField = BattleField(pilots: [noobPilot])let expectation = XCTDarwinNotificationExpectation(notificationName: "ͬͯΈΔ͞ʂ")battleField.startBattle()wait(for: [expectation], timeout: 3)
͓·͚Darwin Notify͏ͱΞϓϦؒແࢹͯ͠௨͕Ͱ͖·͢ɻϦδΣΫτϦεΫݱࡏௐࠪதɺ·ͨվΊͯ͠·͢ɻͯ࣍ճ
Ҏ্