When starting a new app, I always bring these NotificationCenter protocols into the project.
The idea is that by having these protocols, we can inject the NotificationCenter to the classes/structs instead of using the .default
instance.
By doing so, our ability to add unit tests to the code around notifications dramatically increases.
Protocols
Poster
Observer
Publisher
Testing
Then in the testing target, we can create the Mock class of the NotificationCenter that we will inject into our sut
s.
Example
ViewModel
import Combine
final class ViewModel: ObservableObject {
@Published var count = 0
private var notificationHandler: NotificationPublisherProtocol
private var subscribers: [AnyCancellable] = []
init(notificationHandler: NotificationPublisherProtocol = NotificationCenter.default) {
self.notificationHandler = notificationHandler
addObservers()
}
private func addObservers() {
notificationHandler.publisher(for: .someNotification).sink { [weak self] _ in
// Every time the notification is published, we increase the count by 1.
self?.count += 1
}
.store(in: &subscribers)
}
}
extension Notification.Name {
static let someNotification = Notification.Name("someNotification")
}
ViewModelTests
import XCTest
final class ViewModelTests: XCTestCase {
private var sut: ViewModel!
private var notificationCenter: NotificationCenterMock!
override func setUp() {
super.setUp()
notificationCenter = .init()
sut = .init(notificationHandler: notificationCenter)
}
func test_when_notificationIsPosted_countIncreases() {
// Given
XCTAssertEqual(sut.count, 0)
XCTAssertEqual(notificationCenter.postCalls, 0)
// When
notificationCenter.post(name: .someNotification)
// Then
XCTAssertEqual(sut.count, 1)
XCTAssertEqual(notificationCenter.postCalls, 1)
XCTAssertEqual(
notificationCenter.postedNotifications,
[.someNotification]
)
}
}
✅