如何用RxTest测试ViewModel中重复购买请求的过滤逻辑
问题描述
我们正在为ViewModel编写单元测试,需求是:当ViewModel收到requestInAppPayment输入请求时,仅当实例属性isPurchasing为false时才会执行订阅(用于防止购买按钮重复点击)。想要测试「购买进行中点击购买按钮,请求被过滤」的场景,但测试结果不符合预期——期望仅触发一次成功事件,实际却触发了两次。
ViewModel代码
requestInAppPayment .filter { [weak self] _ in self?.isPurchasing == false } .subscribe(with: self, onNext: { owner, inAppPayment in owner.isPurchasing = true owner.showIndicator.onNext(()) owner.requestInAppPurchaseUseCase.execute(productID: inAppPayment.productID) }) .disposed(by: disposeBag) requestInAppPurchaseUseCase.purchaseSuccess .do(onNext: { [weak self] _ in self?.isPurchasing = false self?.hideIndicator.onNext(()) })
单元测试代码
let observer = scheduler.createObserver(String.self) let stubPaymentItem = InAppPayment() scheduler.createHotObservable([ .next(10, stubPaymentItem), .next(20, stubPaymentItem) ]) .bind(to: viewModel.input.requestInAppPurchase) .disposed(by: disposeBag) viewModel.output.showPaymentResultPage .map { "success" } .drive(observer) .disposed(by: disposeBag) scheduler.start() // 实际结果: [.next(10, "success"), .next(20, "success")] // 预期结果: [.next(10, "success")] XCTAssertEqual(observer.events, [ .next(10, "success") ])
问题原因
测试中未正确控制requestInAppPurchaseUseCase.purchaseSuccess的发送时机。默认情况下,execute方法可能同步触发了purchaseSuccess事件,导致第一次购买完成后立刻将isPurchasing设为false,此时第二次点击请求到达时,isPurchasing已经回到false,所以请求没有被过滤,最终触发了两次成功事件。
解决方案
需要模拟requestInAppPurchaseUseCase的行为,让purchaseSuccess事件在第二次点击请求之后再发送,确保第二次点击时isPurchasing仍为true,从而验证过滤逻辑。
修改后的单元测试代码示例:
let observer = scheduler.createObserver(String.self) let stubPaymentItem = InAppPayment() // 模拟UseCase的purchaseSuccess事件,在30时刻发送(晚于第二次点击的20时刻) let mockPurchaseSuccess = scheduler.createHotObservable([ .next(30, ()) ]) // 将模拟的事件绑定到UseCase的purchaseSuccess上(假设该属性是可绑定的Subject) mockPurchaseSuccess.bind(to: viewModel.requestInAppPurchaseUseCase.purchaseSuccess) .disposed(by: disposeBag) scheduler.createHotObservable([ .next(10, stubPaymentItem), .next(20, stubPaymentItem) ]) .bind(to: viewModel.input.requestInAppPurchase) .disposed(by: disposeBag) viewModel.output.showPaymentResultPage .map { "success" } .drive(observer) .disposed(by: disposeBag) scheduler.start() // 此时第二次点击(20时刻)时,isPurchasing仍为true,请求被过滤 XCTAssertEqual(observer.events, [ .next(10, "success") ])
另外,也可以确保requestInAppPurchaseUseCase.execute的逻辑是异步的,或者在测试中控制其执行时机,避免purchaseSuccess同步触发导致isPurchasing提前重置。
内容的提问来源于stack exchange,提问作者Jiss
相关产品推荐
相关产品推荐

