2

It's my first time using RxTest and I am struggling how to do the following approach:

protocol ViewModelType {
    func transform(input: ViewModel.Input) -> ViewModel.Output
}

struct ViewModel: ViewModelType {
    private let isLoading = PublishSubject<Bool>()

    struct Input {
        let trigger: PublishSubject<Void>
    }

    struct Output {
        let someAction: Observable<Void>
        let isLoading: Observable<Bool>
    }

    func transform(input: Input) -> Output {
        let someAction = input
            .trigger
            .do(onNext: { _ in
                self.isLoading.onNext(true)
                //do some async task
                self.isLoading.onNext(false)
            })
        return Output(someAction: someAction, isLoading: isLoading)
    }
}

I have created a Publish Subject inside the viewModel to notify the view when it should show the loader or not.

Everything works fine, excepts I don't know how to test it with the RxTest framework.

I was trying to use the scheduler and cold observables but couldn't manage to make it work.

What I would like to have:

  1. With the scheduler send the .next(10, ()) to the trigger.
  2. Somehow record the events of the isLoading and assert equal that goes first true and then false. Like that: [.next(10, true), .next(20, false)].

Maybe, the isLoading the way I did it, it's not testable. But seems it's going out through the Output I think maybe there is some way.

Thank you so much, if something is unclear, please feel free to edit or guide me to a better question. Much appreciated.

Eironeia
  • 771
  • 8
  • 20

1 Answers1

2

A couple of things:

Your Input struct should contain Observables, not subjects. That way you can attach to them properly.

You don't want to use the do operator. Instead think about the problem from the output first. When trigger emits you want isLoading to emit true and you want the async task to start. That means you should have two observable chains. There's lots of sample code showing how to do this.

In the mean time, here's your test (along with the required modifications to your code:


class RxSandboxTests: XCTestCase {

    func testOne() {

        let scheduler = TestScheduler(initialClock: 0)
        let trigger = scheduler.createHotObservable([.next(10, ())])
        let someActionResult = scheduler.createObserver(Bool.self)
        let isLoadingResult = scheduler.createObserver(Bool.self)
        let bag = DisposeBag()
        let sut = ViewModel()
        let input = ViewModel.Input(trigger: trigger.asObservable())
        let output = sut.transform(input: input)

        bag.insert(
            output.someAction.map { true }.bind(to: someActionResult),
            output.isLoading.bind(to: isLoadingResult)
        )
        scheduler.start()

        XCTAssertEqual(someActionResult.events, [.next(10, true)])
        XCTAssertEqual(isLoadingResult.events, [.next(10, true), .next(10, false)])
    }

}

protocol ViewModelType {
    func transform(input: ViewModel.Input) -> ViewModel.Output
}

struct ViewModel: ViewModelType {
    private let isLoading = PublishSubject<Bool>()

    struct Input {
        let trigger: Observable<Void>
    }

    struct Output {
        let someAction: Observable<Void>
        let isLoading: Observable<Bool>
    }

    func transform(input: Input) -> Output {
        let someAction = input
            .trigger
            .do(onNext: { _ in
                self.isLoading.onNext(true)
                //do some async task
                self.isLoading.onNext(false)
            })
        return Output(someAction: someAction, isLoading: isLoading)
    }
}

Daniel T.
  • 32,821
  • 6
  • 50
  • 72
  • Hello Daniel, thanks a lot for your answer, I am going to review it on detail now, the PublishSubject on the Input it's a bad copy paste, it's actually an Observable, thanks for pointing it out though. I will be back to you in a moment. – Eironeia Jun 25 '19 at 16:09
  • 1
    You can make a coldObservable into an Observable too. The difference between a hot and cold test observable is that with a hot one, the emission times are absolute while with a cold one, they are relative to when the observable was subscribed too. – Daniel T. Jun 25 '19 at 21:21
  • I write it down, thanks again for the clarification! – Eironeia Jun 26 '19 at 06:54