如何对与Core Bluetooth API交互的代码进行单元测试?

6
我想对一个作为CBPeripheralManagerDelegateCBPeripheralManager类的类进行单元测试。通常,为了模拟外部类依赖关系,我会通过类初始化器或属性传递的方式使用依赖注入的形式。处理基于单例的API时,我能够使用像Kiwi这样的库来模拟返回单例的类级方法(即[ClassName stub:@selector(sharedInstance) andReturn:myStubbedInstance])。在模拟CBPeripheralManager的情况下的问题在于,它的初始化器需要委托实例。因此,任何使用我的类的代码都需要像这样做:
PeripheralManagerWrapper *wrapper = [[PeripheralManagerWrapper alloc] init];
CBPeripheralManager *peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:wrapper queue:nil options:nil];
wrapper.peripheralManager = peripheralManager;

然后,为了对我的PeripheralManagerWrapper类进行单元测试,我可以简单地实例化它并传入模拟的CBPeripheralManager。然而,我不希望要求任何包装对象的调用代码都必须经过这个设置。有没有更好的模式来处理这种情况?我已经使用过Kiwi和OCMockito,但除了可能对CBPeripheralManagerallocinit方法进行存根,然后在PeripheralManagerWrapper 的初始化器中实例化该实例之外,它们似乎都没有提供这种功能。
1个回答

4
在我看来,Core Bluetooth API 很适合单元测试。所有的代理回调都需要管理器和相关参数,因此如果你按照这种模式使用这些参数而不是内部状态,那么你就可以传入任何想要的东西。使用模拟对象是做到这一点的最佳方法。在单元测试期间,不应该尝试模拟管理器的行为。你应该专注于验证你的代码与 API 的交互而不要做更多的事情。
包装器可能更适合集成测试。但实际上,根据我的经验,Core Bluetooth 代码的集成测试最好手动完成。堆栈不够稳定,无法进行可靠的测试,测试代码也必须针对堆栈错误进行加强,这非常困难,因为很显然,这些错误没有被文档记录或通过查看 API 而能预测得到。而另一方面,你的测试代码也必须模拟堆栈的错误行为。虽然可能有这样的情况发生,但测试代码将会和你正在测试的代码一样复杂,甚至更复杂。

是的,这个包装器并不是真正用于测试,而是为了将蓝牙行为与ViewController隔离开来。Wrapper可能不是最好的后缀。 - Michael McGuire
1
@allprog 附加问题:我正准备测试CBCentralManager和CBPeripheral等委托。但是CBPeripheral / CBService / CBCharacteristic具有隐藏的初始化方法,因此无法实例化。那么它是否可以进行模拟? (顺便说一句,我正在使用Swift-这会是一个额外的挑战吗?)谢谢 :) - Jens Schwarzer
1
@JensSchwarzer 这里列出了一些ObjC示例代码,展示了这是可能的 here。不幸的是,我对Swift没有足够的经验,但搜索没有弹出任何解决方案(我猜你也是因为这个原因而问的 :))。将业务逻辑与CB领域分离可能是解决方案。这样,您将使用自己的类型,CB限制将不会受到限制。甚至可能没有其他方法在swift中对CB代码进行单元测试。很抱歉我不能提供更多帮助。 - allprog
1
这就是答案存在的意义吧,我想。 - Kane Cheshire
1
这里又有一个链接。虽然这是一个老问题,但也许这个链接能帮助到某些人。它提供了一些有用的建议,只要你能忍受表情符号的干扰。单元测试万岁 - Victor Engel
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接