ios – How to setup testing the removal of a block-based observer in an object’s deinit?


Say I have this code:

class Foo {

    private let nc: NotificationCenter
    
    init(nc: NotificationCenter = .default) {
        self.nc = nc
        
        nc.addObserver(forName: Notification.Name.Foo, object: nil, queue: .main) { _ in
            Task { [weak self] in
                self?.doSomething()
            }
        }
    }

    deinit {
        nc.removeObserver(self, name: Notification.Name.Foo, object: nil)
    }

    @objc
    private func doSomething() async {
        // triggers some async code
    }
}

I want to write a unit test to ensure this observer is removed when the class is deallocated.

The Apple docs specify:

If you used addObserver(forName:object:queue:using:) to create your observer, you should call this method or removeObserver(_:name:object:) before the system deallocates any object that addObserver(forName:object:queue:using:) specifies.

I’ve mocked a NotificationCenter:

class MockNotificationCenter: NotificationCenter {

    var notifications: [NSNotification.Name?] = []

    override func addObserver(
        forName name: NSNotification.Name?,
        object obj: Any?,
        queue: OperationQueue?,
        using block: @escaping (Notification) -> Void) -> NSObjectProtocol
    {
        notifications.append(name)
        return super.addObserver(forName: name, object: obj, queue: queue, using: block)
    }

    override func removeObserver(
        _ observer: Any, 
        name aName: NSNotification.Name?, 
        object anObject: Any?
    ) {
        notifications = notifications.filter { $0 != aName }
        super.removeObserver(observer, name: aName, object: anObject)
    }

And here’s my unit test:

    func testDeinit_DoesRemoveObserver() {
        // Given
        var sut: Foo?
        let mockNC = MockNotificationCenter()
        // When
        sut = Foo(notificationCenter: mockNC)
        // Then
        XCTAssertEqual(mockNC.notifications.count, 1) // Succeeds
        // When
        sut = nil
        // Then
        XCTAssertEqual(mockNC.notifications.count, 0) // Fails
    }

XCTAssertEqual failed: (“1”) is not equal to (“0”)

How would I fix this to assert that an observer has been removed from the notification center upon deallocation of my observing object?

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img