测试 NSLog 输出
Test for NSLog output
我正在编写一个框架,其中还包括一个可以在运行时 enabled/disabled 的简单记录器。因为我想对尽可能多的框架进行单元测试,所以我也想测试记录器是否正常工作。
记录器只是通过 NSLog
进行记录。现在我需要测试输出是否真的符合预期(即它是否真的记录并且输出格式正确)。我找不到使用 Xcode 的 XCTest 框架来做到这一点的方法。
我可以修改记录器,使其在测试时不使用 NSLog
,但我觉得这很容易出错。那么,是否有更好的方法来检查 NSLog
输出?
有记录器输出字符串。让另一个对象打印这些。这样您就可以将第一个对象与第二个对象分开进行单元测试。
我通过编写一个实用程序 class 解决了这个问题,该实用程序将文件描述符重定向到文件并能够重置此重定向:
@interface IORedirector : NSObject
- (instancetype)initWithFileDescriptor:(int)fileDescriptor targetPath:(NSURL *)fileURL flags:(int)oflags mode:(mode_t)mode;
- (void)reset;
@end
@implementation IORedirector
{
int _fileDescriptor;
int _savedFileDescriptor;
}
- (instancetype)initWithFileDescriptor:(int)fileDescriptor targetPath:(NSURL *)fileURL flags:(int)oflags mode:(mode_t)mode
{
_savedFileDescriptor = dup(fileDescriptor);
if (_savedFileDescriptor == -1) {
NSLog(@"Could not save file descriptor %d: %s", fileDescriptor, strerror(errno));
return nil;
}
int tempFD = open(fileURL.path.fileSystemRepresentation, oflags, mode);
if (tempFD == -1) {
NSLog(@"Could not open %@: %s", fileURL.path, strerror(errno));
close(_savedFileDescriptor);
return nil;
}
if (close(fileDescriptor) == -1) {
NSLog(@"Closing file descriptor %d failed: %s", fileDescriptor, strerror(errno));
close(_savedFileDescriptor);
close(tempFD);
return nil;
}
if (dup2(tempFD, fileDescriptor) == -1) {
NSLog(@"Could not replace file descriptor %d with new one for %@: %s", fileDescriptor, fileURL.path, strerror(errno));
close(_savedFileDescriptor);
close(tempFD);
return nil;
}
_fileDescriptor = fileDescriptor;
close(tempFD);
return self;
}
- (void)dealloc
{
[self reset];
}
- (void)reset
{
if (_savedFileDescriptor == -1) return;
if (close(_fileDescriptor) == -1) {
NSLog(@"Closing file descriptor %d failed: %s", _fileDescriptor, strerror(errno));
return;
}
if (dup2(_savedFileDescriptor, _fileDescriptor) == -1) {
NSLog(@"Could not restore file descriptor %d: %s", _fileDescriptor, strerror(errno));
return;
}
close(_savedFileDescriptor);
_savedFileDescriptor = -1;
}
@end
初始化器 returns 一个有效的实例(已建立重定向)或 nil
。立即调用 reset
恢复重定向。
初始化程序和 reset
都可能失败,"lose" 原始文件描述符,但 window 很小且不太可能。这可能仅在多线程应用程序中使用此 class 时相关(我不这样做),因此如果您打算在多线程环境中使用它,您需要确保只有一个初始化程序或 reset
一次可以 运行 使用全局互斥锁或其他东西。
使用这个 class(当然,我有一个单独的测试用例),我可以将 STDERR_FILENO
重定向到一个文件,从而捕获 NSLog
的输出.这样我就可以测试未修改的日志记录 class.
创建一个合适的临时文件路径作为练习留给 reader。
我正在编写一个框架,其中还包括一个可以在运行时 enabled/disabled 的简单记录器。因为我想对尽可能多的框架进行单元测试,所以我也想测试记录器是否正常工作。
记录器只是通过 NSLog
进行记录。现在我需要测试输出是否真的符合预期(即它是否真的记录并且输出格式正确)。我找不到使用 Xcode 的 XCTest 框架来做到这一点的方法。
我可以修改记录器,使其在测试时不使用 NSLog
,但我觉得这很容易出错。那么,是否有更好的方法来检查 NSLog
输出?
有记录器输出字符串。让另一个对象打印这些。这样您就可以将第一个对象与第二个对象分开进行单元测试。
我通过编写一个实用程序 class 解决了这个问题,该实用程序将文件描述符重定向到文件并能够重置此重定向:
@interface IORedirector : NSObject
- (instancetype)initWithFileDescriptor:(int)fileDescriptor targetPath:(NSURL *)fileURL flags:(int)oflags mode:(mode_t)mode;
- (void)reset;
@end
@implementation IORedirector
{
int _fileDescriptor;
int _savedFileDescriptor;
}
- (instancetype)initWithFileDescriptor:(int)fileDescriptor targetPath:(NSURL *)fileURL flags:(int)oflags mode:(mode_t)mode
{
_savedFileDescriptor = dup(fileDescriptor);
if (_savedFileDescriptor == -1) {
NSLog(@"Could not save file descriptor %d: %s", fileDescriptor, strerror(errno));
return nil;
}
int tempFD = open(fileURL.path.fileSystemRepresentation, oflags, mode);
if (tempFD == -1) {
NSLog(@"Could not open %@: %s", fileURL.path, strerror(errno));
close(_savedFileDescriptor);
return nil;
}
if (close(fileDescriptor) == -1) {
NSLog(@"Closing file descriptor %d failed: %s", fileDescriptor, strerror(errno));
close(_savedFileDescriptor);
close(tempFD);
return nil;
}
if (dup2(tempFD, fileDescriptor) == -1) {
NSLog(@"Could not replace file descriptor %d with new one for %@: %s", fileDescriptor, fileURL.path, strerror(errno));
close(_savedFileDescriptor);
close(tempFD);
return nil;
}
_fileDescriptor = fileDescriptor;
close(tempFD);
return self;
}
- (void)dealloc
{
[self reset];
}
- (void)reset
{
if (_savedFileDescriptor == -1) return;
if (close(_fileDescriptor) == -1) {
NSLog(@"Closing file descriptor %d failed: %s", _fileDescriptor, strerror(errno));
return;
}
if (dup2(_savedFileDescriptor, _fileDescriptor) == -1) {
NSLog(@"Could not restore file descriptor %d: %s", _fileDescriptor, strerror(errno));
return;
}
close(_savedFileDescriptor);
_savedFileDescriptor = -1;
}
@end
初始化器 returns 一个有效的实例(已建立重定向)或 nil
。立即调用 reset
恢复重定向。
初始化程序和 reset
都可能失败,"lose" 原始文件描述符,但 window 很小且不太可能。这可能仅在多线程应用程序中使用此 class 时相关(我不这样做),因此如果您打算在多线程环境中使用它,您需要确保只有一个初始化程序或 reset
一次可以 运行 使用全局互斥锁或其他东西。
使用这个 class(当然,我有一个单独的测试用例),我可以将 STDERR_FILENO
重定向到一个文件,从而捕获 NSLog
的输出.这样我就可以测试未修改的日志记录 class.
创建一个合适的临时文件路径作为练习留给 reader。