swift 如何在RxTest中测试多个事件

nsc4cvqm  于 2023-06-28  发布在  Swift
关注(0)|答案(1)|浏览(121)

我们目前正在为ViewModel实现单元测试。
当一个requestInAppPayment Input请求从视图模型传入时,只有当示例属性isPurchasing过滤器为false时才会实现订阅(以防止多次触摸购买按钮)。
我想写一个测试用例的过滤器时,购买按钮被按下,而购买正在进行中,但我不知道如何做到这一点。
我想写一个测试用例,当购买按钮被点击而购买正在进行时,它会进行过滤。

在ViewMode下

  1. requestInAppPayment
  2. .filter { [weak self] _ in
  3. self?.isPurchasing == false
  4. }
  5. .subscribe(with: self, onNext: { owner, inAppPayment in
  6. owner.isPurchasing = true
  7. owner.showIndicator.onNext(())
  8. owner.requestInAppPurchaseUseCase.execute(productID: inAppPayment.productID)
  9. })
  10. .disposed(by: disposeBag)
  11. requestInAppPurchaseUseCase.purchaseSuccess
  12. .do(onNext: { [weak self] _ in
  13. self?.isPurchasing = false
  14. self?.hideIndicator.onNext(())
  15. })

单元测试中

  1. let observer = scheduler.createObserver(String.self)
  2. let stubPaymentItem = InAppPayment()
  3. scheduler.createHotObservable([
  4. .next(10, stubPaymentItem),
  5. .next(20, stubPaymentItem)
  6. ])
  7. .bind(to: viewModel.input.requestInAppPurchase)
  8. .disposed(by: disposeBag)
  9. viewModel.output.showPaymentResultPage
  10. .map { "success" }
  11. .drive(observer)
  12. .disposed(by: disposeBag)
  13. scheduler.start()
  14. // result = [.next(10, "success"), .next(20, "success")],
  15. // The logic I think is [.next(10, "success")]
  16. XCTAssertEqual(observer.events, [
  17. .next(10, "success")
  18. ])
pxiryf3j

pxiryf3j1#

下面是一个完整的工作示例,它基于您仅提供的一点单元测试。我根本没有使用您的视图模型代码,我只是编写了一个通过测试的视图模型。
我使用了我在这里提供的测试工具:https://gist.github.com/danielt1263/bd449100764e3166644f7a38bca86c96

  1. class ViewModelTests: XCTestCase {
  2. func test() {
  3. let scheduler = TestScheduler(initialClock: 0)
  4. let disposeBag = DisposeBag()
  5. let iapObserver = scheduler.createObserver(InAppPayment.self)
  6. let mockUseCase = MockRIAP(scheduler: scheduler)
  7. let viewModel = ViewModel(requestInAppPurchaseUseCase: mockUseCase)
  8. // I had to add all of the above to make the test compile.
  9. let observer = scheduler.createObserver(String.self)
  10. let stubPaymentItem = InAppPayment(id: 25) // I had to give the object an ID
  11. scheduler.createObservable(timeline: "-AA", values: ["A": stubPaymentItem]) // waits one second then emits two stubPaymentItems one second apart.
  12. .bind(to: viewModel.input.requestInAppPurchase)
  13. .disposed(by: disposeBag)
  14. viewModel.output.showPaymentResultPage
  15. .map { "success" }
  16. .drive(observer)
  17. .disposed(by: disposeBag)
  18. scheduler.start()
  19. XCTAssertEqual(observer.events, [
  20. .next(2, "success") // success emits at the 2 second mark because the trigger waits a second and then the mock waits a second. The second trigger is ignored.
  21. ])
  22. }
  23. }
  24. class MockRIAP: RequestInAppPurchaseUseCase {
  25. let args: TestableObserver<InAppPayment.ID>
  26. let _execute: (InAppPayment.ID) -> Observable<IAPResponse>
  27. init(scheduler: TestScheduler) {
  28. args = scheduler.createObserver(InAppPayment.ID.self)
  29. _execute = scheduler.mock(args: args, values: ["A": IAPResponse()], timelineSelector: { _ in "-A|" }) // waits one second then emits an IAPResponse
  30. }
  31. func execute(id: InAppPayment.ID) -> RxSwift.Observable<IAPResponse> {
  32. _execute(id)
  33. }
  34. }

下面是使上述工作的生产代码:

  1. protocol RequestInAppPurchaseUseCase {
  2. func execute(id: InAppPayment.ID) -> Observable<IAPResponse>
  3. }
  4. struct ViewModel {
  5. struct Input {
  6. let requestInAppPurchase: AnyObserver<InAppPayment>
  7. }
  8. struct Output {
  9. let showPaymentResultPage: Driver<Void> // a Driver<Void> doesn't make sense. Why would you want to emit the previous result? Better would be a Signal<Void>.
  10. }
  11. let input: Input
  12. let output: Output
  13. }
  14. extension ViewModel {
  15. init(requestInAppPurchaseUseCase: RequestInAppPurchaseUseCase) {
  16. let _requestInAppPurchase = PublishSubject<InAppPayment>()
  17. let showResults = _requestInAppPurchase
  18. .flatMapFirst { purchase in
  19. requestInAppPurchaseUseCase.execute(id: purchase.id)
  20. }
  21. .map { _ in }
  22. .asDriver(onErrorDriveWith: .empty())
  23. input = Input(requestInAppPurchase: _requestInAppPurchase.asObserver())
  24. output = Output(showPaymentResultPage: showResults)
  25. }
  26. }
  27. struct InAppPayment: Identifiable {
  28. let id: Int
  29. }
  30. struct IAPResponse { }
展开查看全部

相关问题