CombineのPublisherをテストするためのexpect/expectErrorメソッドを作成した

おそらく現在はユニットテストでCombineのPublisherの値を検査するためにはXCTestExpectationを駆使したりしないといけません（簡便な方法があったら教えて欲しいです。）。さらに私はSwiftCheckを利用しているので検査結果をBool値で取得したいという要求があります。

ですのでテストターゲットでexpectとexpectErrorという検査メソッドをPublisherに追加しました。

```swift
import XCTest
import Combine

public extension Publisher where Output: Equatable {
    @discardableResult
    func expect(_ expectedValue: Output,
                takesNewest: Bool = false,
                timeout: TimeInterval = 2.0,
                fulfillmentCount: Int = 1,
                file: StaticString = #file,
                function: StaticString = #function,
                line: UInt = #line) -> Bool {
        var result                 = false
        var actualValues           = [Output]()
        var actualFulfillmentCount = 0
        var cancellables           = Set<AnyCancellable>()
        let exp                    = XCTestExpectation()
        exp.expectedFulfillmentCount = fulfillmentCount
        exp.assertForOverFulfill = true

        let waiter = XCTWaiter()

        self.receive(on: RunLoop.main).sink(receiveCompletion: { _ in }, receiveValue: {
            if takesNewest || !result {
                result = $0 == expectedValue
            }
            actualValues.append($0)
            actualFulfillmentCount += 1
            exp.fulfill()
        }).store(in: &cancellables)

        _ = waiter.wait(for: [exp], timeout: timeout)

        XCTAssertLessThanOrEqual(fulfillmentCount,
                                 actualFulfillmentCount,
                                 "\(file) - \(function):\(line): Expectation is short of fulfillment. '\(fulfillmentCount)' expected, but '\(actualFulfillmentCount)'.")

        XCTAssertTrue(result,
                      "\(file) - \(function):\(line): Expected output is '\(String(describing: expectedValue))', but actual stream is '\(String(describing: actualValues))'")

        return result && fulfillmentCount <= actualFulfillmentCount
    }
}

public extension Publisher where Failure: Equatable {
    @discardableResult
    func expectError(_ expectedError: Failure,
                     timeout: TimeInterval = 2.0,
                     file: StaticString = #file,
                     function: StaticString = #function,
                     line: UInt = #line) -> Bool {
        var actualError: Failure?
        var cancellables = Set<AnyCancellable>()
        let exp          = XCTestExpectation()

        let waiter = XCTWaiter()

        self.receive(on: RunLoop.main).sink(receiveCompletion: { completion in
            switch completion {
            case .failure(let receivedError):
                actualError = receivedError
            default: ()
            }

            exp.fulfill()
        }, receiveValue: { _ in }).store(in: &cancellables)

        _ = waiter.wait(for: [exp], timeout: timeout)

        XCTAssertEqual(expectedError, actualError,
                       "\(file) - \(function):\(line): Expected error is '\(String(describing: expectedError))', but actual error is '\(String(describing: actualError))'")

        return expectedError == actualError
    }
}
```

expectする値が取得できれば成功、できなければ失敗、そして結果のBool値を返すようになっています。
timeoutは値を取得する制限時間です。
expectのtakesNewestは、trueであればストリームの最新の値まで全て検査し、過去に該当する値があっても結果を上書きします。fulfillmentCountは何回値を取得するかを設定します。

テストで利用するには以下のように記述します。

```swift
class MyTests: XCTestCase {
    class P: Publisher {
        typealias Output = Int
        typealias Failure = Never
        
        func receive<S>(subscriber: S) where S : Subscriber, P.Failure == S.Failure, P.Output == S.Input {
            DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
                subscriber.receive(1)
                subscriber.receive(2)
                subscriber.receive(completion: .finished)
            }
            
            let subscription = Subscriptions.empty
            subscriber.receive(subscription: subscription)
        }
    }
    
    class Q: Publisher {
        typealias Output = Int
        typealias Failure = MyError
        
        func receive<S>(subscriber: S) where S : Subscriber, Q.Failure == S.Failure, Q.Output == S.Input {
            DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
                subscriber.receive(completion: .failure(.error1))
            }
            
            let subscription = Subscriptions.empty
            subscriber.receive(subscription: subscription)
        }
    }

    
    enum MyError: Error, Equatable {
        case error1
        case error2
    }


    func testExpect() {
        P().expect(1)
        P().buffer(size: 2, prefetch: .keepFull, whenFull: .dropOldest).collect().expect([1, 2])
        
        P().expect(1, takesNewest: true, fulfillmentCount: 2) // failed
        P().expect(3) // failed
    }
    
    func testExpectError() {
        Q().expectError(.error1)
    }
}
```

私のプロジェクトでは一応動作しているようです。
バグやより良い実装があれば教えてください。