使用原始数组/指针参数验证 Objective-C 中的方法调用
Posted
技术标签:
【中文标题】使用原始数组/指针参数验证 Objective-C 中的方法调用【英文标题】:Verifying method calls in Objective-C with primitive array/pointer arguments 【发布时间】:2013-01-21 18:07:54 【问题描述】:我正在测试的类中有两种方法:
- (NSUInteger)sendBuffer:(uint8_t *)buffer length:(NSUInteger)length;
- (BOOL)sendFormattedCommandForAddress:(uint8_t)address
withData:(uint8_t)data
andCommandType:(ZKZSensorCommandType)commandType;
-sendFormattedCommandForAddress:withData:andCommandType:
构建一个char
数组并将指向该数组的指针传递给-sendBuffer:length:
。此数组的内容因address
、data
和commandType
而异。在我的测试中,我想验证是否构建了正确的数组并将其传递给-sendBuffer:length:
。 OCMock 不会让我对 uint8_t *
参数设定期望。 OCMockito/OCHamcrest 不会让我使用部分模拟(还)。我尝试将-sendBuffer:length:
方法与调用我的测试用例类中的方法的方法混合,并对该方法调用设置期望。但是,当调用 swizzled 方法时,self
指向被测类而不是我的测试用例。我可以在我的测试类中保留缓冲区,然后在我的测试中检查这个缓冲区的内容,但我讨厌在生产代码中添加一些东西来支持测试。有没有人对如何测试这种行为有更好的建议?
【问题讨论】:
【参考方案1】:虽然这可以说是一个见仁见智的问题,但综合单元测试的好处使得代码复杂性的小幅且包含的增量成为值得的权衡。在您描述的情况下,我认为您向生产类添加缓冲区以便可以在单元测试中检索和断言发送的最后一个字节序列的想法是一种非常好的方法。
我个人可能会添加一个布尔属性来控制是否维护缓冲区,只是为了避免在生产代码中最终发送大消息时任何意外的不必要的内存开销。单元测试设置当然会设置此属性,而应用程序/框架生产代码则不会。同样,关键是使添加的代码如此简单,以至于它显然是正确的。 :-)
如果您希望或需要严格控制生产类公开的接口,您还可以考虑使缓冲区维护代码依赖于条件编译。我个人更喜欢前一种方法,但每个人都有自己的方法。
【讨论】:
【参考方案2】:您不需要模拟框架来制作模拟。我认为挑战在于您实际上不想致电-sendBuffer:length:
。在这种情况下,只需使用 Michael Feathers 的 Working Effectively with Legacy Code 一书中描述的 Subclass 和 Override Method。
不知道您的班级名称,我将其命名为 Sender。这就是我要写的,直接放到 SenderTest.m 中:
@interface TestingSender : Sender
@property (assign, nonatomic) NSUInteger sendBufferCount;
@property (assign, nonatomic) uint8_t *sendBufferBuffer;
@property (assign, nonatomic) NSUInteger sendBufferLength;
@end
@implementation TestingSender
- (NSUInteger)sendBuffer:(uint8_t *)buffer length:(NSUInteger)length
++_sendBufferCount;
_sendBufferBuffer = buffer;
_sendBufferLength = length;
@end
然后测试代码会创建一个 TestingSender 而不是一个 Sender。
【讨论】:
以上是关于使用原始数组/指针参数验证 Objective-C 中的方法调用的主要内容,如果未能解决你的问题,请参考以下文章
C 语言数组 ( 指针退化验证 | 计算数组大小 | #define LENGTH(array) (sizeof(array) / sizeof(*array)) )