如何比较INStartCallContactResolutionResult对象进行单元测试?



我试图单元测试我的意图处理程序类INStartCallIntent,但我有麻烦比较结果对象的接触分辨率。

例如,给定INStartCallIntent的基本处理程序:

import Intents
class StartCallHandler: NSObject, INStartCallIntentHandling {
func resolveContacts(for intent: INStartCallIntent, with completion: @escaping ([INStartCallContactResolutionResult]) -> Void) {
guard let contacts = intent.contacts, !contacts.isEmpty, let person = contacts.first else {
completion([.needsValue()])
return
}

guard contacts.count == 1 else {
completion([.unsupported(forReason: .multipleContactsUnsupported)])
return
}
let matchingContacts = [person] // matching logic here
switch matchingContacts.count {
case 2  ... Int.max:
// We need Siri's help to ask user to pick one from the matches.
completion([.disambiguation(with: matchingContacts)])
case 1:
// We have exactly one matching contact
completion([.success(with: person)])
default:
completion([.unsupported(forReason: .noContactFound)])
}
}
}

如果我创建一个简单的单元测试,我无法比较INStartCallContactResolutionResult对象:

func testResolveContacts() {
let person = INPerson(personHandle: INPersonHandle(value: nil, type: .unknown), nameComponents: nil, displayName: "Steve Jobs", image: nil, contactIdentifier: nil, customIdentifier: nil)
let intent = INStartCallIntent(audioRoute: .unknown, destinationType: .unknown, contacts: [person], recordTypeForRedialing: .unknown, callCapability: .audioCall)
let handler = StartCallHandler()

handler.resolveContacts(for: intent) { result in
XCTAssertEqual(result.count, 1)
guard let firstResult = result.first else { return XCTFail() }

let expectedPerson = INPerson(personHandle: INPersonHandle(value: nil, type: .unknown), nameComponents: nil, displayName: "Steve Jobs", image: nil, contactIdentifier: nil, customIdentifier: nil)
let expectedResult = INStartCallContactResolutionResult(.success(with: expectedPerson))
XCTAssertEqual(firstResult, expectedResult)
}
}

XCTAssertEqual失败,显示如下消息:

xctasserequal failed: ("{resolutionResultCode = Success;INPerson: 0x600002c7b780>{displayName =乔布斯;contactIdentifier =;nameComponents =;Image =;customIdentifier =;关系=;siriMatches =;INPersonHandle: 0x600000d78960>{Value =;类型=未知;Label =;};};disambiguationItems =;itemToConfirm =;unsupportereason = 0;}")不等于("{resolutionResultCode = Success;INPerson: 0x600002c7b900>{displayName =乔布斯;contactIdentifier =;nameComponents =;Image =;customIdentifier =;关系=;siriMatches =;INPersonHandle: 0x600000d78d80>{Value =;类型=未知;Label =;};};disambiguationItems =;itemToConfirm =;unsupportereason = 0;}")

因此,即使两个对象具有相同的属性,XCTAssertEqual也会失败,大概是因为Apple端没有实现相等函数。

这使得测试这个函数几乎不可能。有人能用其他方法完成这个吗?

我最终在这里做的是将联系人解析器逻辑放在一个单独的助手类中,并将INStartCallContactResolutionResult类包装成一个自定义enum,本质上只是将其1:1映射。

public enum PersonResolverUnsupportedReason {
case startCallContactUnsupportedReason(INStartCallContactUnsupportedReason)
}
public enum PersonResolverResult {
case success(INPerson)
case disambiguation([INPerson])
case needsValue
case unsupported
case unsupportedWithReason(PersonResolverUnsupportedReason)
case skip

var startCallContactResolutionResult: INStartCallContactResolutionResult {
switch self {
case let .success(person):
return .success(with: person)
case let .disambiguation(persons):
return .disambiguation(with: persons)
case .needsValue:
return .needsValue()
case .unsupported:
return .unsupported()
case let .unsupportedWithReason(reason):
switch reason {
case let .startCallContactUnsupportedReason(startCallReason):
return .unsupported(forReason: startCallReason)
}
case .skip:
return .notRequired()
}
}
}
public protocol PersonResolverProtocol: AnyObject {
func attemptToResolvePerson(_ person: INPerson, with: @escaping ([PersonResolverResult]) -> Void)
}
public class PersonResolver: PersonResolverProtocol {
public func attemptToResolvePerson(_ person: INPerson, with completion: @escaping ([PersonResolverResult]) -> Void) {
let matchingContacts = [person] // matching logic here
switch matchingContacts.count {
case 2...Int.max:
completion([.disambiguation(matchingContacts.map { INPerson(...) })])
case 1:
guard let matchingContact = matchingContacts.first else {
completion([.unsupportedWithReason(.startCallContactUnsupportedReason(.noContactFound))])
break
}
completion([.success(INPerson(...))])
default:
// no contacts match
completion([.unsupportedWithReason(.startCallContactUnsupportedReason(.noContactFound))])
}
}
}

现在我可以:

  • 单元测试接触解析器逻辑更好
  • 将解析器注入StartCallHandler
  • 如果需要,在其他意图处理程序类中重用联系人解析器类。

最新更新