Testing Mastery
DateProviding: Control Time in Tests
March 23, 2026
5 min read

Now let's build the mock that makes time-based tests deterministic.
Follow along with the code: iOS-Practice on GitHub
The MockDateProvider
class MockDateProvider: DateProviding {
var now: Date
init(now: Date = Date()) {
self.now = now
}
// Helper to create dates easily
func setNow(year: Int, month: Int, day: Int, hour: Int = 12) {
var components = DateComponents()
components.year = year
components.month = month
components.day = day
components.hour = hour
now = Calendar.current.date(from: components)!
}
}
Testing Subscription Expiration
class SubscriptionManagerTests: XCTestCase {
var mockDateProvider: MockDateProvider!
var sut: SubscriptionManager!
override func setUp() {
mockDateProvider = MockDateProvider()
sut = SubscriptionManager(dateProvider: mockDateProvider)
}
func test_isSubscriptionValid_whenNotExpired_returnsTrue() {
// Arrange: It's June 1st
mockDateProvider.setNow(year: 2024, month: 6, day: 1)
let subscription = makeSubscription(
expiresYear: 2024, expiresMonth: 12, expiresDay: 31
)
// Act
let isValid = sut.isSubscriptionValid(subscription)
// Assert
XCTAssertTrue(isValid)
}
func test_isSubscriptionValid_whenExpired_returnsFalse() {
// Arrange: It's June 1st, subscription expired Jan 1st
mockDateProvider.setNow(year: 2024, month: 6, day: 1)
let subscription = makeSubscription(
expiresYear: 2024, expiresMonth: 1, expiresDay: 1
)
// Act
let isValid = sut.isSubscriptionValid(subscription)
// Assert
XCTAssertFalse(isValid)
}
}
Testing the 7-Day Reminder
func test_shouldShowRenewalReminder_whenExpiresIn7Days_returnsTrue() {
// Arrange: June 1st, expires June 8th = 7 days
mockDateProvider.setNow(year: 2024, month: 6, day: 1)
let subscription = makeSubscription(
expiresYear: 2024, expiresMonth: 6, expiresDay: 8
)
// Act & Assert
XCTAssertTrue(sut.shouldShowRenewalReminder(subscription))
}
func test_shouldShowRenewalReminder_whenExpiresIn8Days_returnsFalse() {
// Arrange: June 1st, expires June 9th = 8 days (too early)
mockDateProvider.setNow(year: 2024, month: 6, day: 1)
let subscription = makeSubscription(
expiresYear: 2024, expiresMonth: 6, expiresDay: 9
)
// Act & Assert
XCTAssertFalse(sut.shouldShowRenewalReminder(subscription))
}
func test_shouldShowRenewalReminder_whenExpiresToday_returnsFalse() {
// Arrange: Already expired (0 days left)
mockDateProvider.setNow(year: 2024, month: 6, day: 1)
let subscription = makeSubscription(
expiresYear: 2024, expiresMonth: 6, expiresDay: 1
)
// Act & Assert
XCTAssertFalse(sut.shouldShowRenewalReminder(subscription))
}
Test Helper
func makeSubscription(
expiresYear: Int,
expiresMonth: Int,
expiresDay: Int
) -> Subscription {
var components = DateComponents()
components.year = expiresYear
components.month = expiresMonth
components.day = expiresDay
components.hour = 23
components.minute = 59
let expirationDate = Calendar.current.date(from: components)!
return Subscription(
id: "test_sub",
planName: "Pro",
expirationDate: expirationDate,
createdAt: Date().addingTimeInterval(-86400 * 30)
)
}
Key Insight
With MockDateProvider, you can test any point in time:
- Expired yesterday
- Expires today
- Expires in exactly 7 days
- Leap year boundaries
- Year transitions
The tests run the same way every time, regardless of when they execute.