Notify user of credential failure and make another attempt The below code works because of the do operator on line 24. Is there a more functional way to get the test to pass, or would you consider this an appropriate use of do?

import RxSwift


struct Token: Equatable {
    let rawValue: String
    static func ==(lhs: Token, rhs: Token) -&gt; Bool {
        return lhs.rawValue == rhs.rawValue
    }
}

protocol Login {
    func getCredentials() -&gt; Observable&lt;(email: String, password: String)&gt;
    func presentAuthFailure(_ error: Observable&lt;Error&gt;)
}

protocol Network {
    func getAuthToken(credentials: (email: String, password: String)) -&gt; Observable&lt;Token&gt;
}

// MARK: - function Under Test
func checkCredentials(login: Login, network: Network) -&gt; Observable&lt;Token&gt; {
    let credentials = login.getCredentials()
    let token = credentials.flatMap { network.getAuthToken(credentials: $0) }
        .do(onError: { login.presentAuthFailure(Observable.just($0)) }) // can this be done in some other way?
    return token.retry()
}

// MARK: - Test harness
class MockLogin: Login {
    func getCredentials() -&gt; Observable&lt;(email: String, password: String)&gt; {
        assert(_credentials == nil)
        _credentials = PublishSubject()
        return _credentials!
    }

    func presentAuthFailure(_ error: Observable&lt;Error&gt;) {
        _ = error.subscribe(onNext: { [unowned self] error in
            assert(self._error == nil)
            self._error = error
        })
    }

    var _credentials: PublishSubject&lt;(email: String, password: String)&gt;?
    var _error: Error?
}

class MockNetwork: Network {
    func getAuthToken(credentials: (email: String, password: String)) -&gt; Observable&lt;Token&gt; {
        assert(_token == nil)
        _token = PublishSubject()
        return _token!
    }

    var _token: PublishSubject&lt;Token&gt;?
}

func testTwoBadAttempts(login: MockLogin, network: MockNetwork) {
    checkCredentials(login: login, network: network).subscribe()

    assert(login._credentials != nil)

    let credentials = (email: "foo", password: "bar")
    login._credentials?.onNext(credentials)

    assert(network._token != nil)

    let error = NSError(domain: "testing", code: -1, userInfo: nil)
    network._token?.onError(error)

    assert(login._error != nil)

    network._token = nil
    login._error = nil

    login._credentials?.onNext(credentials)

    network._token?.onError(error)

    assert(login._error != nil)
}

let login = MockLogin()
let network = MockNetwork()
testTwoBadAttempts(login: login, network: network)
I found the solution to removing the do. I needed to move the error presentation into the flatMap and use materialize so that a network error wouldn't break the observable chain.

// MARK: - function Under Test
func checkCredentials(login: Login, network: Network) -&gt; Observable&lt;Token&gt; {
    let credentials = login.getCredentials()
    let token = credentials.flatMap { (credential) -&gt; Observable&lt;Token&gt; in
        let token = network.getAuthToken(credentials: credential)
            .materialize()
        let error = token.map { $0.error }.filter { $0 != nil }.map { $0! }
        login.presentAuthFailure(error)
        return token.map { $0.element }.filter { $0 != nil }.map { $0! }
    }
    return token
}