4

Say I have a Field class with a harvest function like this:

class Field {

    func harvest(handler: (Vegetable) -> Void) {
        …
        handler(carrot)
        …
        handler(potato)
        …
        handler(carrot)
        …
    }
}

I also have a Reactive version/API for the same function:

import RxSwift

extension Reactive where Base: Field {

    func harvest() -> Observable<Vegetable> {
        return Observable.create { observer in
            self.base.harvest(handler: observer.onNext)

            return Disposables.create()
        }
    }
}

For testing purposes, I created a subclass of Field named MockField that overrides harvest(:) to invoke the handler with a set of stubbed Vegetables. When using the MockField object like field.harvest(:) everything works fine and I get the stubbed vegetables.

Now I want to do the same with the Reactive extension to stub calls to field.rx.harvest, but I cannot override it to return stubbed Vegetables! How can I override functions in the rx namespace?

Luis Valdés
  • 1,409
  • 1
  • 12
  • 12

1 Answers1

2

Probably the easiest way to do this would not be subclassing, but defining harvest() -> Observable<Vegetable> in terms of Field.harvest(handler:)

import RxSwift

extension Reactive where Base: Field {

    func harvest() -> Observable<Vegetable> {
        return Observable.create { observer in
            self.base.harvest(handler: observer.onNext)

            return Disposables.create()
        }
    }
}

Doing it this way you will not need to maintain two implementations of harvest.

PS: The block passed to a disposable should cancel the action, not send the completed event. If there is no facility to cancel harvest(handler:), you can simply return Disposables.create().

tomahh
  • 13,441
  • 3
  • 49
  • 70
  • Thanks for the comment! That's exactly how I am doing it, invoking the base implementation, but I didn't write it like that in the example (my fault, I will update it). However, the issue is still there. How do you override the implementation of `rx.harvest()` while testing to return stubbed `Vegetable`s? I think it is not possible to override it as it is a Reactive extension. One solution would be to extend `Field` with `rx_harvest` (not `rx.harvest`), so I can override it for testing, but then I loose the Reactive grouping. Any thoughts? – Luis Valdés Dec 13 '16 at 09:06
  • With the above implementation, you should already have the `MockVegetable` as values in the observable if used like `MockField().rx.harvest()`, right? Also, what happens when doing `extension Reactive where base: MockField { func harvest() -> Observable { /* ... */ } }`? – tomahh Dec 13 '16 at 09:14
  • I am doing `extension Reactive where base: MockField { func harvest() -> Observable { return Observable.from([ Vegetable, … ]) } }` to return the stubbed `Vegetable`s, and I inject `MockField` to the class that uses it. However, as `MockField` is not overriding `Field`'s `rx.harvest`, the mock implementation is never called. – Luis Valdés Dec 13 '16 at 09:22