【Swift】ゼロからのCombineフレームワーク - ユニットテストを書いてみる
Combineを使ったユニットテストの方法
2つの方法を試してみました。
- ライブラリなしでやる
- Entwineというテスト補助用のライブラリを使う
テスト対象コード
incrementCounter: PassthroughSubject
のsend
メソッドが呼ばれたら、自身のcounter: Int
に数値を加えて、counterStr: CurrentValueSubject
を更新する単純なモデルです。
テストコードでは、incrementCounter
のsend
メソッドの呼び出しにたいして、counterStr
が正しく更新されていることをテストします。
import Combine
import Foundation
protocol CounterViewModelProtocol {
var incrementCounter: PassthroughSubject<Int, Never> { get }
var counterStr: CurrentValueSubject<String, Never>! { get }
}
class CounterViewModel: CounterViewModelProtocol {
var incrementCounter: PassthroughSubject<Int, Never> = .init()
var counterStr: CurrentValueSubject<String, Never>!
private var counter: Int = 0
private var cancellables = Set<AnyCancellable>()
init() {
counterStr = CurrentValueSubject("\(counter)")
incrementCounter
.sink(receiveValue: { [weak self] increment in
if let self = self {
self.counter += increment
self.counterStr.send("\(self.counter)")
}
}).store(in: &cancellables)
}
}
ライブラリなしでテストする
How to Test Your Combine Publishersを参考にしました。
テスト補助用のexpectValue
というメソッドにPublisher
と期待される値の配列を渡して、wait
します。
func testCounterStr() {
let viewModel = CounterViewModel()
let expectValues = ["0", "2", "5"]
let result = expectValue(of: viewModel.counterStr, equals: expectValues)
viewModel.incrementCounter.send(2)
viewModel.incrementCounter.send(3)
wait(for: [result.expectation], timeout: 1)
}
テスト補助用のメソッド
extension XCTestCase {
typealias CompetionResult = (expectation: XCTestExpectation, cancellable: AnyCancellable)
func expectValue<T: Publisher>(
of publisher: T,
timeout: TimeInterval = 2,
file: StaticString = #file,
line: UInt = #line,
equals: [T.Output]
) -> CompetionResult where T.Output: Equatable {
let exp = expectation(description: "Correct values of " + String(describing: publisher))
var mutableEquals = equals
let cancellable = publisher
.sink(receiveCompletion: { _ in },
receiveValue: { value in
if value == mutableEquals.first {
mutableEquals.remove(at: 0)
if mutableEquals.isEmpty {
exp.fulfill()
}
}
})
return (exp, cancellable)
}
}
Entwineを使ってテストする
テスト用に用意されたTestScheduler
を使って、テスト対象のSubject
のsend
メソッド呼び出しのタイミングを設定したあと、resume
メソッドを呼び出します。
TestableSubscriber
をテスト対象のPublisher
にreceive
することで、TestableSubscriber
のrecordedOutput
にイベントが記録されます。
func testCounterStrWithEntWine() {
let scheduler = TestScheduler(initialClock: 0)
let incrementCounter = viewModel.incrementCounter
scheduler.schedule(after: 100) { incrementCounter.send(2) }
scheduler.schedule(after: 200) { incrementCounter.send(3) }
let subscriber = scheduler.createTestableSubscriber(String.self, Never.self)
viewModel.counterStr.receive(subscriber: subscriber)
scheduler.resume()
let expected: TestSequence<String, Never> = [
(000, .subscription),
(000, .input("0")),
(100, .input("2")),
(200, .input("5")),
]
XCTAssertEqual(subscriber.recordedOutput, expected)
}
参考
Author And Source
この問題について(【Swift】ゼロからのCombineフレームワーク - ユニットテストを書いてみる), 我々は、より多くの情報をここで見つけました https://qiita.com/turara/items/f92d3a9a6904ada1b73a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .