You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为仅含类方法的FileManager实现依赖注入以测试AttachmentsRepository

如何给仅含类方法的FileManager做依赖注入(用于测试)

看起来你想给自定义的FileManager类(全是类方法那种)做依赖注入,方便测试AttachmentsRepository对吧?你提到的“ex...”应该是指**提取协议(Extract Protocol)**的方案,这正是OC里处理这类场景的标准做法,我给你一步步拆解具体实现:

步骤1:抽象协议,解耦依赖

首先我们需要把FileManager里用到的类方法抽象成一个协议,这样AttachmentsRepository只需要依赖这个协议,而不是具体的FileManager类:

// FileManagerProtocol.h
#import <Foundation/Foundation.h>

@protocol FileManagerProtocol <NSObject>
// 把你需要用到的FileManager类方法转成协议的实例方法
- (void)writeData:(NSData *)data toFile:(NSString *)filePath;
// 如果还有其他需要测试的类方法,都加到这里
@end

步骤2:让原FileManager遵循协议

因为你的FileManager只有类方法,我们可以让它遵循上面的协议,用实例方法来包装自己的类方法:

// FileManager.m
#import "FileManager.h"
#import "FileManagerProtocol.h"

@implementation FileManager

// 原有的类方法实现
+ (void)writeData:(NSData *)data toFile:(NSString *)filePath {
    // 你的原有逻辑
}

// 实现协议的实例方法,内部调用类方法
- (void)writeData:(NSData *)data toFile:(NSString *)filePath {
    [FileManager writeData:data toFile:filePath];
}

// 其他协议方法同理包装
@end

步骤3:修改AttachmentsRepository,支持注入

接下来修改AttachmentsRepository,添加一个遵循FileManagerProtocol的属性,并且提供带参数的初始化方法,同时保留默认初始化(用原FileManager实例),不影响原有业务代码:

// AttachmentsRepository.h
#import <Foundation/Foundation.h>
#import "FileManagerProtocol.h"

@interface AttachmentsRepository : NSObject
@property (nonatomic, strong) id<FileManagerProtocol> fileManager;

// 带注入参数的初始化方法
- (instancetype)initWithFileManager:(id<FileManagerProtocol>)fileManager;
// 原有业务方法
- (void)newAttachment:(NSData *)attachmentData;
@end
// AttachmentsRepository.m
#import "AttachmentsRepository.h"

@implementation AttachmentsRepository

- (instancetype)initWithFileManager:(id<FileManagerProtocol>)fileManager {
    self = [super init];
    if (self) {
        _fileManager = fileManager;
    }
    return self;
}

// 默认初始化,使用原FileManager实例
- (instancetype)init {
    return [self initWithFileManager:[[FileManager alloc] init]];
}

- (void)newAttachment:(NSData *)attachmentData {
    NSString *generatedPath = @"/some/generated/path";
    // 现在调用协议方法,而不是直接调用FileManager的类方法
    [self.fileManager writeData:attachmentData toFile:generatedPath];
}

@end

步骤4:创建Mock类用于测试

现在我们可以写一个Mock类遵循FileManagerProtocol,用来验证AttachmentsRepository是否正确调用了文件写入方法:

// MockFileManager.h
#import <Foundation/Foundation.h>
#import "FileManagerProtocol.h"

@interface MockFileManager : NSObject <FileManagerProtocol>
// 用来验证方法是否被调用
@property (nonatomic, assign) BOOL didCallWriteData;
// 用来存储传递的参数,方便验证
@property (nonatomic, strong) NSData *passedData;
@property (nonatomic, strong) NSString *passedFilePath;
@end
// MockFileManager.m
#import "MockFileManager.h"

@implementation MockFileManager

- (void)writeData:(NSData *)data toFile:(NSString *)filePath {
    self.didCallWriteData = YES;
    self.passedData = data;
    self.passedFilePath = filePath;
}

@end

步骤5:在测试用例中注入Mock

最后在测试代码里,把MockFileManager注入到AttachmentsRepository,就可以轻松验证交互逻辑了:

// AttachmentsRepositoryTests.m
#import <XCTest/XCTest.h>
#import "AttachmentsRepository.h"
#import "MockFileManager.h"

@interface AttachmentsRepositoryTests : XCTestCase
@property (nonatomic, strong) AttachmentsRepository *repo;
@property (nonatomic, strong) MockFileManager *mockFileManager;
@end

@implementation AttachmentsRepositoryTests

- (void)setUp {
    [super setUp];
    // 初始化Mock和Repository,注入Mock
    self.mockFileManager = [[MockFileManager alloc] init];
    self.repo = [[AttachmentsRepository alloc] initWithFileManager:self.mockFileManager];
}

- (void)testNewAttachmentWritesDataCorrectly {
    // 准备测试数据
    NSData *testData = [@"test_attachment" dataUsingEncoding:NSUTF8StringEncoding];
    
    // 调用业务方法
    [self.repo newAttachment:testData];
    
    // 验证Mock的状态,确认方法被正确调用
    XCTAssertTrue(self.mockFileManager.didCallWriteData, @"文件写入方法应该被调用");
    XCTAssertEqualObjects(self.mockFileManager.passedData, testData, @"传递的数据不匹配");
    XCTAssertEqualObjects(self.mockFileManager.passedFilePath, @"/some/generated/path", @"文件路径不匹配");
}

@end

为什么这种方案有效?

原来的代码直接依赖FileManager的类方法,耦合度极高,无法在测试时替换实现。通过抽象协议,我们把AttachmentsRepository的依赖从具体类转为抽象协议,这样在测试时可以轻松注入Mock对象,验证它是否正确调用了文件操作方法,而不需要真正操作文件系统,让测试更快、更可靠。

内容的提问来源于stack exchange,提问作者h3dkandi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:21:08