测试 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。