Last active
January 25, 2026 15:12
-
-
Save fxm90/106fd802f869d3d259d672d0416b66fa to your computer and use it in GitHub Desktop.
Example on how to elegantly test a delegate protocol.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // ScreenOverlayViewModel.swift | |
| // | |
| // Created by Felix Mau on 12.10.19. | |
| // Copyright © 2019 Felix Mau. All rights reserved. | |
| // | |
| import Foundation | |
| protocol ScreenOverlayViewModelDelegate: AnyObject { | |
| func screenOverlayViewModelDidUpdate(alpha: CGFloat) | |
| } | |
| final class ScreenOverlayViewModel { | |
| // MARK: - Public Properties | |
| weak var delegate: ScreenOverlayViewModelDelegate? | |
| // MARK: - Public Methods | |
| func showOverlay() { | |
| delegate?.screenOverlayViewModelDidUpdate(alpha: 1.0) | |
| } | |
| func hideOverlay() { | |
| delegate?.screenOverlayViewModelDidUpdate(alpha: 0.0) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // ScreenOverlayViewModelTestCase.swift | |
| // | |
| // Created by Felix Mau on 12.10.19. | |
| // Copyright © 2019 Felix Mau. All rights reserved. | |
| // | |
| import Foundation | |
| import XCTest | |
| /// - Note: See `ScreenOverlayViewModelTests` for the Swift Testing variant of these tests. | |
| final class ScreenOverlayViewModelTestCase: XCTestCase { | |
| // MARK: - Private Properties | |
| private var delegateMock: ScreenOverlayViewModelDelegateMock! | |
| private var viewModel: ScreenOverlayViewModel! | |
| // MARK: - Test Lifecycle | |
| override func setUp() { | |
| super.setUp() | |
| delegateMock = ScreenOverlayViewModelDelegateMock() | |
| viewModel = ScreenOverlayViewModel() | |
| viewModel.delegate = delegateMock | |
| } | |
| override func tearDown() { | |
| viewModel = nil | |
| delegateMock = nil | |
| super.tearDown() | |
| } | |
| // MARK: - Test Method `showOverlay()` | |
| func test_showOverlay_shouldInformDelegate_toUpdateAlpha_toOne() { | |
| // When | |
| viewModel.showOverlay() | |
| // Then | |
| XCTAssertEqual(delegateMock.invokedMethod, .screenOverlayViewModelDidUpdate(alpha: 1.0)) | |
| } | |
| // MARK: - Test Method `hideOverlay()` | |
| func test_hideOverlay_shouldInformDelegate_toUpdateAlpha_toZero() { | |
| // When | |
| viewModel.hideOverlay() | |
| // Then | |
| XCTAssertEqual(delegateMock.invokedMethod, .screenOverlayViewModelDidUpdate(alpha: 0.0)) | |
| } | |
| } | |
| // MARK: - Mocks | |
| private final class ScreenOverlayViewModelDelegateMock: ScreenOverlayViewModelDelegate { | |
| // MARK: - Types | |
| enum DelegateMethod: Equatable { | |
| case screenOverlayViewModelDidUpdate(alpha: CGFloat) | |
| } | |
| // MARK: - Public properties | |
| private(set) var invokedMethod: DelegateMethod? | |
| // MARK: - `ScreenOverlayViewModelDelegate` Protocol | |
| func screenOverlayViewModelDidUpdate(alpha: CGFloat) { | |
| invokedMethod = .screenOverlayViewModelDidUpdate(alpha: alpha) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // ScreenOverlayViewModelTests.swift | |
| // | |
| // Created by Felix Mau on 25.01.26. | |
| // Copyright © 2026 Felix Mau. All rights reserved. | |
| // | |
| import Foundation | |
| import Testing | |
| /// - Note: See `ScreenOverlayViewModelTestCase` for the XCTest variant of these tests. | |
| struct ScreenOverlayViewModelTests { | |
| // MARK: - Private Properties | |
| private let delegateMock: ScreenOverlayViewModelDelegateMock | |
| private let viewModel: ScreenOverlayViewModel | |
| // MARK: - Initializer | |
| init() { | |
| delegateMock = ScreenOverlayViewModelDelegateMock() | |
| viewModel = ScreenOverlayViewModel() | |
| viewModel.delegate = delegateMock | |
| } | |
| // MARK: - Test Method `showOverlay()` | |
| @Test | |
| func showOverlay_shouldInformDelegate_toUpdateAlpha_toOne() { | |
| // When | |
| viewModel.showOverlay() | |
| // Then | |
| #expect(delegateMock.invokedMethod == .screenOverlayViewModelDidUpdate(alpha: 1.0)) | |
| } | |
| // MARK: - Test Method `hideOverlay()` | |
| @Test | |
| func hideOverlay_shouldInformDelegate_toUpdateAlpha_toZero() { | |
| // When | |
| viewModel.hideOverlay() | |
| // Then | |
| #expect(delegateMock.invokedMethod == .screenOverlayViewModelDidUpdate(alpha: 0.0)) | |
| } | |
| } | |
| // MARK: - Mocks | |
| private final class ScreenOverlayViewModelDelegateMock: ScreenOverlayViewModelDelegate { | |
| // MARK: - Types | |
| enum DelegateMethod: Equatable { | |
| case screenOverlayViewModelDidUpdate(alpha: CGFloat) | |
| } | |
| // MARK: - Public properties | |
| private(set) var invokedMethod: DelegateMethod? | |
| // MARK: - `ScreenOverlayViewModelDelegate` Protocol | |
| func screenOverlayViewModelDidUpdate(alpha: CGFloat) { | |
| invokedMethod = .screenOverlayViewModelDidUpdate(alpha: alpha) | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In case you need to verify multiple delegate methods are called, you can change the type of the property
invokedMethodto an array:And in the delegate protocol method use
append()to add all invoked methods to that array: