基于Viper架构的Swift & iOS单元测试方法咨询
Viper架构登录模块单元测试实践建议
Viper架构的优势就是各组件职责清晰,单元测试可以按组件拆分针对性开展,以下是具体的测试方向和实践方法:
1. 优先测试LoginPresenter(核心逻辑层)
Presenter是View与Interactor的中间枢纽,业务逻辑集中,是单元测试的核心对象:
- 验证View交互触发:比如调用
presenter.loginButtonTapped(username: "", password: "")后,检查是否正确调用View协议的方法(如showLoading()、showError(message:)),可以通过Mock View类来统计方法调用次数和参数是否符合预期。 - 验证Interactor调用逻辑:确认Presenter在合适时机(如输入合法时)调用Interactor的
login(username:password:)方法,且参数传递准确。 - 验证Interactor回调处理:模拟Interactor返回登录成功/失败的结果,检查Presenter是否正确触发后续逻辑(如成功时调用Router跳转、失败时通知View显示错误)。
示例代码片段(Swift + XCTest):
class MockLoginView: LoginViewProtocol { var showLoadingCalled = false var receivedErrorMessage: String? func showLoading() { showLoadingCalled = true } func showError(message: String) { receivedErrorMessage = message } } func testLoginWithEmptyCredentials() { let mockView = MockLoginView() let presenter = LoginPresenter(view: mockView) presenter.loginButtonTapped(username: "", password: "") XCTAssertEqual(mockView.receivedErrorMessage, "请输入账号和密码") }
2. 测试LoginInteractor(业务逻辑层)
Interactor负责具体业务处理(如本地校验、网络请求封装),测试需隔离外部依赖:
- 本地校验逻辑测试:单独测试空账号、密码长度不足、格式错误等场景,验证Interactor是否返回对应的错误类型。
- 网络请求逻辑测试:用Mock网络客户端替代真实网络层,模拟请求成功/失败的情况,检查Interactor是否正确回调Presenter的
loginDidSucceed()或loginDidFail(error:)方法。 - 注意:绝对不要在单元测试中发起真实网络请求,所有外部依赖必须Mock,保证测试的稳定性和速度。
3. 测试LoginViewController(UI交互层)
ViewController主要负责UI事件转发和状态更新,测试重点是交互的正确性:
- 控件事件转发测试:模拟点击登录按钮、输入框内容变化等操作,验证ViewController是否正确调用Presenter对应的方法,且参数与输入内容一致。
- UI状态更新测试:通过Mock Presenter触发View协议方法,检查ViewController是否正确更新UI(如显示加载指示器、弹出错误提示框),必要时可使用异步测试保证UI更新完成后再断言。
4. 测试LoginRouter(路由层)
Router负责页面跳转逻辑,测试重点是跳转行为是否符合预期:
- 登录成功跳转测试:模拟Presenter调用
router.navigateToHome(),验证Router是否正确初始化目标ViewController,并通过Mock NavigationController确认push/pop操作是否触发。 - 其他跳转场景测试:比如测试点击"忘记密码"按钮时,是否正确跳转到对应的重置密码页面。
通用测试原则
- 单一职责:每个测试方法只覆盖一个场景(如单独测试空账号、空密码两种情况),避免测试用例过于复杂。
- 依赖隔离:所有外部依赖(网络、数据库、其他业务模块)都用Mock/Stub替代,确保测试不受外部环境影响。
- 异步处理:涉及异步操作(如网络请求)的测试,必须使用
XCTestExpectation等待异步任务完成后再执行断言,避免测试结果不稳定。
内容的提问来源于stack exchange,提问作者ismailp
相关产品推荐
相关产品推荐

