您應該解耦您的測試需求,以等待GUI更新從主代碼路徑的運行方式運行。在您發佈的第一個代碼塊中,dispatch_sync
幾乎肯定是錯誤的方法(與dispatch_async
),因爲您將無緣無故地阻止在主線程中等待的後臺線程(在dispatch_sync
之後沒有代碼),這可能會導致線程匱乏(在部署中)。我猜你已經做了dispatch_sync
試圖使用隊列本身來連接兩個並行任務。如果你是真正致力於使用有所次優的方法,你可以做這樣的事情:
- (void)testOne
{
SOAltUpdateView* view = [[SOAltUpdateView alloc] initWithFrame: NSMakeRect(0, 0, 100, 100)];
STAssertNotNil(view, @"View was nil");
STAssertEqualObjects(view.color, [NSColor redColor] , @"Initial color was wrong");
dispatch_queue_t q = dispatch_queue_create("test", 0);
dispatch_group_t group = dispatch_group_create();
view.queue = q;
// Run the operation
[view update];
// An operation we can wait on
dispatch_group_async(group, q, ^{ });
while (dispatch_group_wait(group, DISPATCH_TIME_NOW))
{
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, YES);
}
STAssertEqualObjects(view.color, [NSColor greenColor] , @"Updated color was wrong");
view.queue = nil;
[view release];
dispatch_release(group);
dispatch_release(q);
}
這是似乎最接近你已經有什麼辦法,但我想出了一些可能是更好/更清潔一點:信號量可以爲你做到這一點,只需一點點努力,你就可以將侵入你的實際GUI代碼變得非常小。 (注意:根本不可能有任何入侵,因爲爲了讓兩個並行任務互鎖,他們必須共享一些東西來聯繫,在 - 共享 - 在你現有的代碼中,它是隊列,這裏我使用了一個信號量。)考慮一下這個人爲的例子:我爲測試工具添加了一個通用的方法來推入一個信號量,當後臺操作完成時它可以用來通知它。被測代碼的「入侵」限於兩個宏。
NSObject的+ AsyncGUITestSupport.h:
@interface NSObject (AsyncGUITestSupport)
@property (nonatomic, readwrite, assign) dispatch_semaphore_t testCompletionSemaphore;
@end
#define OPERATION_BEGIN(...) do { dispatch_semaphore_t s = self.testCompletionSemaphore; if (s) dispatch_semaphore_wait(s, DISPATCH_TIME_NOW); } while(0)
#define OPERATION_END(...) do { dispatch_semaphore_t s = self.testCompletionSemaphore; if (s) dispatch_semaphore_signal(s); } while(0)
NSObject的+ AsyncGUITestSupport。L:
#import "NSObject+AsyncGUITestSupport.h"
#import <objc/runtime.h>
@implementation NSObject (AsyncGUITestSupport)
static void * const kTestingSemaphoreAssociatedStorageKey = (void*)&kTestingSemaphoreAssociatedStorageKey;
- (void)setTestCompletionSemaphore:(dispatch_semaphore_t)myProperty
{
objc_setAssociatedObject(self, kTestingSemaphoreAssociatedStorageKey, myProperty, OBJC_ASSOCIATION_ASSIGN);
}
- (dispatch_semaphore_t)testCompletionSemaphore
{
return objc_getAssociatedObject(self, kTestingSemaphoreAssociatedStorageKey);
}
@end
SOUpdateView.h
@interface SOUpdateView : NSView
@property (nonatomic, readonly, retain) NSColor* color;
- (void)update;
@end
SOUpdateView.m
#import "SOUpdateView.h"
#import "NSObject+AsyncGUITestSupport.h"
@implementation SOUpdateView
{
NSUInteger _count;
}
- (NSColor *)color
{
NSArray* colors = @[ [NSColor redColor], [NSColor greenColor], [NSColor blueColor] ];
@synchronized(self)
{
return colors[_count % colors.count];
}
}
- (void)drawRect:(NSRect)dirtyRect
{
[self.color set];
NSRectFill(dirtyRect);
}
- (void)update
{
OPERATION_BEGIN();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
@synchronized(self)
{
_count++;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self setNeedsDisplay: YES];
OPERATION_END();
});
});
}
@end
然後測試工具:
#import "TestSOTestGUI.h"
#import "SOUpdateView.h"
#import "NSObject+AsyncGUITestSupport.h"
@implementation TestSOTestGUI
- (void)testOne
{
SOUpdateView* view = [[SOUpdateView alloc] initWithFrame: NSMakeRect(0, 0, 100, 100)];
STAssertNotNil(view, @"View was nil");
STAssertEqualObjects(view.color, [NSColor redColor] , @"Initial color was wrong");
// Push in a semaphore...
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
view.testCompletionSemaphore = sem;
// Run the operation
[view update];
// Wait for the operation to finish.
while (dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW))
{
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, YES);
}
// Clear out the semaphore
view.testCompletionSemaphore = nil;
STAssertEqualObjects(view.color, [NSColor greenColor] , @"Updated color was wrong");
}
@end
希望這有助於。
你不能在另一個線程或隊列上運行單元測試嗎? – 2013-02-20 23:47:09