使用Specta单元测试检测对象是否泄漏

Specta作为一个优秀的测试框架,不仅能够进行常规的单元测试,也能够测试对象是否存在泄漏。

原理

创建一个容器TestContainer,它weak持有将要检测的对象testObject,然后,我们让其他持有testObject的指针都置为nil,这样就只有这个容器TestContainer在weak持有testObject,我们知道没有强引用这个testObject的时候,testObject的对象会被释放掉, 而TestContainer里面weak引用的指针,也会被置为nil。

而我们会延时几秒钟,检查TestContainer的指针是不是为nil。

具体操作

TestContainer 代码

@interface TestContainer : NSObject

@property (nonatomic, weak) id object;

@end


@implementation TestContainer
// Empty
@end

最后我们会延时TestContainer的object指针是不是为nil。

要检测的对象 代码

@interface TestObject : NSObject

@end

@implementation TestObject

@end

Spec测试代码

下面写检测代码,我们定义一个局部变量testObj, 然后在一个@autoreleasepool里面创建对象, 并且这个testObj在block内部置为nil

describe(@"TestObject", ^{
    context(@"when created", ^{
        __block TestObject *testObj = nil;
        it(@"should be dealloc when not use", ^{
            TestContainer * tc = [TestContainer new];
            @autoreleasepool {
                testObj = [TestObject new];
                tc.object = testObj;
                testObj = nil;
            };
            expect(tc.object).after(5).to.beNil();
        });
    });
});

这里我们断言:断定tc的object指针在5秒钟后,是nil。

检测结果

在Xcode中,按Command + U开始测试,上面代码中, 我们运行的结果是 Test Success

修改代码

我们故意修改TestObject代码, 让其不能正常释放。


@interface TestObject : NSObject

@end

@implementation TestObject

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.timer = [NSTimer timerWithTimeInterval:1
                                             target:self
                                           selector:@selector(timerAction)
                                           userInfo:nil
                                            repeats:YES];
    }
    return self;
}

- (void)timerAction {
    NSLog(@"hello");
}

@end

检测结果2

Test Fail

更加模板化一点的用法

从上面的代码中,如果您按照上面的操作进行测试,能够发现我们能够检测对象是否正常释放。

在实际项目中,我们知道我们要测试的对象会有很多, 除了上面的检测代码外, 我们想要的会更多。

作为一个程序员, 想的是如何能够更加简单化!!!

写Spec模板代码

利用sharedExamplesFor模板化


sharedExamplesFor(@"dealloc_behavior", ^(NSDictionary *data) {
    context(@"release", ^{
        it(@"should dealloc", ^{
            id (^block)(void) = [[data allValues] firstObject];
            if (!block) {
                return;
            }
            TestContainer *container = [TestContainer new];
            @autoreleasepool {
                id object = nil;
                __weak id weakObject = nil;
                object = block();
                weakObject = object;
                expect(weakObject).notTo.beNil();
                container.object = weakObject;
                object = nil;
            }
            expect(container.object).after(5).beNil();
        });
    });
});


这里需要注意的是sharedExamplesFor的第一个参数@"dealloc_behavior",会在下面使用。

Spec测试代码

调用sharedExamplesFor模板化的代码


describe(@"TestObject", ^{
    itShouldBehaveLike(@"dealloc_behavior", @{ @"value" : ^{
        return [[TestObject alloc] init];
    } });
});

注意itShouldBehaveLike的第一个参数, 这个参数就是上面记录的@"dealloc_behavior"。

这样就每次使用5行代码, 就能够检测对象是否被正常释放。

更进一步

这里我们只是检测了这个对象testObj是否正常释放,但是有时这个对象testObj里面新生成了对象innerObj,可能这个testObj正常释放了, 但是innerObj却没有释放。

当然我们可以单独检测innerObj是否正常释放,不过更方便有效的是,我们能够遍历检测testObj里面的子对象,把所有的对象都检测一边。这样就能够更加高效一点的检测对象。

你可能感兴趣的:(使用Specta单元测试检测对象是否泄漏)