Skip to content

Instantly share code, notes, and snippets.

@fxm90
Last active January 25, 2026 15:12
Show Gist options
  • Select an option

  • Save fxm90/106fd802f869d3d259d672d0416b66fa to your computer and use it in GitHub Desktop.

Select an option

Save fxm90/106fd802f869d3d259d672d0416b66fa to your computer and use it in GitHub Desktop.
Example on how to elegantly test a delegate protocol.
//
// 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)
}
}
//
// 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)
}
}
//
// 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)
}
}
@fxm90
Copy link
Copy Markdown
Author

fxm90 commented Oct 14, 2019

In case you need to verify multiple delegate methods are called, you can change the type of the property invokedMethod to an array:

private(set) var invokedMethods = [DelegateMethod]()

And in the delegate protocol method use append() to add all invoked methods to that array:

  func screenOverlayViewModelDidUpdate(alpha: CGFloat) {
    invokedMethods.append(
      .screenOverlayViewModelDidUpdate(alpha: alpha)
    )
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment