Unit Tests
==========

## Testing custom operators

RxSwift uses `RxTest` for all operator tests, located in the AllTests-* target inside the project `Rx.xcworkspace`.

This is an example of a typical `RxSwift` operator unit test:

```swift
func testMap_Range() {
    // Initializes test scheduler.
    // Test scheduler implements virtual time that is
    // detached from local machine clock.
    // This enables running the simulation as fast as possible
    // and proving that all events have been handled.
    let scheduler = TestScheduler(initialClock: 0)
    
    // Creates a mock hot observable sequence.
    // The sequence will emit events at designated
    // times, no matter if there are observers subscribed or not.
    // (that's what hot means).
    // This observable sequence will also record all subscriptions
    // made during its lifetime (`subscriptions` property).
    let xs = scheduler.createHotObservable([
        .next(150, 1),  // first argument is virtual time, second argument is element value
        .next(210, 0),
        .next(220, 1),
        .next(230, 2),
        .next(240, 4),
        .completed(300) // virtual time when completed is sent
    ])
    
    // `start` method will by default:
    // * Run the simulation and record all events
    //   using observer referenced by `res`.
    // * Subscribe at virtual time 200
    // * Dispose subscription at virtual time 1000
    let res = scheduler.start { xs.map { $0 * 2 } }
    
    let correctMessages = Recorded.events(
        .next(210, 0 * 2),
        .next(220, 1 * 2),
        .next(230, 2 * 2),
        .next(240, 4 * 2),
        .completed(300)
    )
    
    let correctSubscriptions = [
        Subscription(200, 300)
    ]
    
    XCTAssertEqual(res.events, correctMessages)
    XCTAssertEqual(xs.subscriptions, correctSubscriptions)
}
```

In the case of non-terminating sequences where you don't necessarily care about the event times, You may also use `RxTest`'s `XCTAssertRecordedElements` to assert specific elements have been emitted.
A terminating stop event (e.g. `completed` or `error`) will cause the test to fail.

```swift
func testElementsEmitted() {
    let scheduler = TestScheduler(initialClock: 0)

    let xs = scheduler.createHotObservable([
        .next(210, "RxSwift"),
        .next(220, "is"),
        .next(230, "pretty"),
        .next(240, "awesome")
    ])

    let res = scheduler.start { xs.asObservable() }

    XCTAssertRecordedElements(res.events, ["RxSwift", "is", "pretty", "awesome"])
}
```

## Testing operator compositions (view models, components)

Examples of how to test operator compositions are contained inside `Rx.xcworkspace` > `RxExample-iOSTests` target.

It's easy to define `RxTest` extensions so you can write your tests in a readable way. Provided examples inside `RxExample-iOSTests` are just suggestions on how you can write those extensions, but there are a lot of possibilities on how to write those tests.

```swift
    // expected events and test data
    let (
        usernameEvents,
        passwordEvents,
        repeatedPasswordEvents,
        loginTapEvents,

        expectedValidatedUsernameEvents,
        expectedSignupEnabledEvents
    ) = (
        scheduler.parseEventsAndTimes("e---u1----u2-----u3-----------------", values: stringValues).first!,
        scheduler.parseEventsAndTimes("e----------------------p1-----------", values: stringValues).first!,
        scheduler.parseEventsAndTimes("e---------------------------p2---p1-", values: stringValues).first!,
        scheduler.parseEventsAndTimes("------------------------------------", values: events).first!,

        scheduler.parseEventsAndTimes("e---v--f--v--f---v--o----------------", values: validations).first!,
        scheduler.parseEventsAndTimes("f--------------------------------t---", values: booleans).first!
    )
```

## Integration tests

It is also possible to write integration tests by using `RxBlocking` operators.

Using `RxBlocking`'s `toBlocking()` method, you can block the current thread and wait for the sequence to complete, allowing you to synchronously access its result.

A simple way to test the result of your sequence is using the `toArray` method. It will return an array of all elements emitted once a sequence has completed successfully, or `throw` if an error caused the sequence to terminate.

```swift
let result = try fetchResource(location)
        .toBlocking()
        .toArray()

XCTAssertEqual(result, expectedResult)
```

Another option would be to use the `materialize` operator which lets you more granularly examine your sequence. It will return a `MaterializedSequenceResult` enumeration that could be either `.completed` along with the emitted elements if the sequence completed successfully, or `failed` if the sequence terminated with an error, along with the emitted error.

```swift
let result = try fetchResource(location)
        .toBlocking()
        .materialize()

// For testing the results or error in the case of terminating with error
switch result {
        case .completed:
            XCTFail("Expected result to complete with error, but result was successful.")
        case .failed(let elements, let error):
            XCTAssertEqual(elements, expectedResult)
            XCTAssertErrorEqual(error, expectedError)
        }

// For testing the results in the case of termination with completion
switch result {
        case .completed(let elements):
            XCTAssertEqual(elements, expectedResult)
        case .failed(_, let error):
            XCTFail("Expected result to complete without error, but received \(error).")
        }
```