Testing Combine
class CarViewModelTest: XCTestCase {
var car: Car!
var carViewModel: CarViewModel!
var cancellables: Set<AnyCancellable>!
override func setUp() {
car = Car()
carViewModel = CarViewModel(car: car)
cancellables = []
}
func testCarViewModelEmitsCorrectStrings() {
// determine what kwhInBattery would be after driving 10km
let newValue: Double = car.kwhInBattery - car.kwhPerKilometer * 10
// configure an array of expected output
var expectedValues = [car.kwhInBattery, newValue].map { doubleValue in
return "The car now has \(doubleValue)kwh in its battery"
}
// expectation to be fulfilled when we've received all expected values
let receivedAllValues = expectation(description: "all values received")
// subscribe to the batterySubject to run the test
carViewModel.batterySubject.sink(receiveValue: { value in
guard let expectedValue = expectedValues.first else {
XCTFail("Received more values than expected.")
return
}
guard expectedValue == value else {
XCTFail("Expected received value \(value) to match first expected value \(expectedValue)")
return
}
// remove the first value from the expected values because we no longer need it
expectedValues = Array(expectedValues.dropFirst())
if expectedValues.isEmpty {
// the test is completed when we've received all expected values
receivedAllValues.fulfill()
}
}).store(in: &cancellables)
// call drive to trigger a second value
carViewModel.drive(kilometers: 10)
// wait for receivedAllValues to be fulfilled
waitForExpectations(timeout: 1, handler: nil)
}
}