A Combine Scheduler that executes actions based on a virtual clock.
Ideal for testing custom Publishers or testing time-dependent Pub-sub code.
The scheduler will run scheduled actions when they are due, based on a
“virtual” clock. The schedulers clock can be adjusted using one of these
methods.
run() - run until there are no more actions scheduled
step() - set the time to the due time of the first action that is due
advanceTime(by:) - advance the internal clock by a specified value
setTime(to:) - set the time to a value, the clock starts at .referenceTime
Actions can schedule other actions. The scheduler will run those actions if
they are due, in the same time update call.
Combine provides scheduling publishers through these methods.
delay(for:tolerance:scheduler:options:)
debounce(for:scheduler:options:)
throttle(for:scheduler:latest:)
Tests for code that uses these methods will have to wait for the events
to happen. When using the RunLoop or DispatchQueue schedulers, the test
will take wall-clock time. Meaning that your test takes longer.
The VirtualTimeScheduler allows testing of such code without waiting in
wall-clock time.
Usage
step() finds the next action due and updates the time to that actions due
time and runs all actions due by that new time.
The run method increases the virtual time step-wise until all scheduled
actions have completed.
“3 seconds later” refers to the “virtual time”.
let scheduler = VirtualTimeScheduler()
let cancellable = Just(42)
.delay(for: .seconds(3), scheduler: scheduler)
.measureInterval(using: scheduler)
.sink { value in
print("Received \(value.magnitude) seconds later")
}
print("Before run \(Date())")
scheduler.run()
print("After run \(Date())")
// Before run 2022-01-20 14:13:11 +0000
// Received 3.0 seconds later
// After run 2022-01-20 14:13:11 +0000
The advanceTime(by:) method advances the virtual time and runs
any scheduled actions that are due by the new time.
let scheduler = VirtualTimeScheduler()
let subject = PassthroughSubject<Int, Never>()
let cancellable = subject
.debounce(for: .seconds(1), scheduler: scheduler)
.sink { value in
let timeInterval = scheduler.now.timeIntervalSinceReferenceTime
print("Received \(value), \(timeInterval) seconds later")
}
for value in 0..<4 {
subject.send(value)
scheduler.advanceTime(by: .seconds(0.1))
}
scheduler.advanceTime(by: .seconds(1))
for value in 4..<8 {
subject.send(value)
scheduler.advanceTime(by: .seconds(0.1))
}
scheduler.advanceTime(by: .seconds(1))
_ = cancellable
// Prints:
// Received 3, 1.4 seconds later
// Received 7, 2.8 seconds later
It is also possible to set the time directly using setTime(to:).
Install
Package.swift
Edit the Package.swift file. Add the VirtualTimeScheduler as a dependency:
let package = Package(
name: " ... ",
products: [ ... ],
dependencies: [
.package(url: "https://github.com/berikv/VirtualTimeScheduler.git", from: "1.0.0") // here
],
targets: [
.target(
name: " ... ",
dependencies: [
"VirtualTimeScheduler" // and here
]),
]
)
VirtualTimeScheduler
A Combine Scheduler that executes actions based on a virtual clock. Ideal for testing custom Publishers or testing time-dependent Pub-sub code.
The scheduler will run scheduled actions when they are due, based on a “virtual” clock. The schedulers clock can be adjusted using one of these methods.
run()
- run until there are no more actions scheduledstep()
- set the time to the due time of the first action that is dueadvanceTime(by:)
- advance the internal clock by a specified valuesetTime(to:)
- set the time to a value, the clock starts at.referenceTime
Actions can schedule other actions. The scheduler will run those actions if they are due, in the same time update call.
Combine provides scheduling publishers through these methods.
delay(for:tolerance:scheduler:options:)
debounce(for:scheduler:options:)
throttle(for:scheduler:latest:)
Tests for code that uses these methods will have to wait for the events to happen. When using the RunLoop or DispatchQueue schedulers, the test will take wall-clock time. Meaning that your test takes longer.
The VirtualTimeScheduler allows testing of such code without waiting in wall-clock time.
Usage
step()
finds the next action due and updates the time to that actions due time and runs all actions due by that new time.The
run
method increases the virtual time step-wise until all scheduled actions have completed.“3 seconds later” refers to the “virtual time”.
The
advanceTime(by:)
method advances the virtual time and runs any scheduled actions that are due by the new time.It is also possible to set the time directly using
setTime(to:)
.Install
Package.swift
Edit the Package.swift file. Add the VirtualTimeScheduler as a dependency:
For .xcodeproj projects