Swift Testing for Unit Tests PART 1 (#5119)

* migrated a lot of unit tests to Swift Testing and added a new implementation for deferred fulfillment

more tests migration

Cleaned the code manually to establish some good patterns

more code improvements

some more code improvements

removed empty tests

update project

* more pr suggestions and cleanups

* removed the TestSetup pattern

* fixing claude not reusing tests

* pr suggestion + added indent rule to swiftformat so that we can prevent AIs to change that
This commit is contained in:
Mauro
2026-02-19 16:20:47 +01:00
committed by GitHub
parent fc03e05697
commit 7982aef855
118 changed files with 4630 additions and 4129 deletions
+102 -96
View File
@@ -9,10 +9,11 @@
import Combine
@testable import ElementX
import Foundation
import XCTest
import Testing
@MainActor
class AudioPlayerStateTests: XCTestCase {
@Suite
struct AudioPlayerStateTests {
static let audioDuration = 10.0
private var audioPlayerState: AudioPlayerState!
private var audioPlayerMock: AudioPlayerMock!
@@ -36,39 +37,42 @@ class AudioPlayerStateTests: XCTestCase {
return audioPlayerMock
}
override func setUp() async throws {
init() async {
audioPlayerActionsSubject = .init()
audioPlayerSeekCallsSubject = .init()
audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: Self.audioDuration)
audioPlayerMock = buildAudioPlayerMock()
audioPlayerMock.seekToClosure = { [weak self] progress in
self?.audioPlayerMock.currentTime = Self.audioDuration * progress
audioPlayerMock.seekToClosure = { [audioPlayerMock] progress in
audioPlayerMock?.currentTime = Self.audioDuration * progress
}
}
func testAttach() {
@Test
func attach() {
audioPlayerState.attachAudioPlayer(audioPlayerMock)
XCTAssert(audioPlayerState.isAttached)
XCTAssertEqual(audioPlayerState.playbackState, .loading)
#expect(audioPlayerState.isAttached)
#expect(audioPlayerState.playbackState == .loading)
}
func testDetach() {
@Test
mutating func detach() {
audioPlayerState.attachAudioPlayer(audioPlayerMock)
audioPlayerState.detachAudioPlayer()
XCTAssert(audioPlayerMock.stopCalled)
XCTAssertFalse(audioPlayerState.isAttached)
XCTAssertEqual(audioPlayerState.playbackState, .stopped)
XCTAssertFalse(audioPlayerState.showProgressIndicator)
#expect(audioPlayerMock.stopCalled)
#expect(!audioPlayerState.isAttached)
#expect(audioPlayerState.playbackState == .stopped)
#expect(!audioPlayerState.showProgressIndicator)
}
func testDelayedState() async throws {
@Test
func delayedState() async throws {
audioPlayerState.attachAudioPlayer(audioPlayerMock)
XCTAssert(audioPlayerState.isAttached)
XCTAssertEqual(audioPlayerState.playbackState, .loading)
XCTAssertEqual(audioPlayerState.playerButtonPlaybackState, .stopped)
#expect(audioPlayerState.isAttached)
#expect(audioPlayerState.playbackState == .loading)
#expect(audioPlayerState.playerButtonPlaybackState == .stopped)
let deferred = deferFulfillment(audioPlayerState.$playerButtonPlaybackState) { output in
switch output {
@@ -80,13 +84,14 @@ class AudioPlayerStateTests: XCTestCase {
}
try await deferred.fulfill()
XCTAssertEqual(audioPlayerState.playerButtonPlaybackState, .loading)
#expect(audioPlayerState.playerButtonPlaybackState == .loading)
}
func testOtherActionsAreNotDelayed() async throws {
@Test
func otherActionsAreNotDelayed() async throws {
audioPlayerState.attachAudioPlayer(audioPlayerMock)
XCTAssertEqual(audioPlayerState.playbackState, .loading)
XCTAssertEqual(audioPlayerState.playerButtonPlaybackState, .stopped)
#expect(audioPlayerState.playbackState == .loading)
#expect(audioPlayerState.playerButtonPlaybackState == .stopped)
let deferred = deferFulfillment(audioPlayerState.$playerButtonPlaybackState) { output in
switch output {
@@ -99,53 +104,48 @@ class AudioPlayerStateTests: XCTestCase {
audioPlayerActionsSubject.send(.didStartPlaying)
try await deferred.fulfill()
XCTAssertEqual(audioPlayerState.playbackState, .playing)
XCTAssertEqual(audioPlayerState.playerButtonPlaybackState, .playing)
#expect(audioPlayerState.playbackState == .playing)
#expect(audioPlayerState.playerButtonPlaybackState == .playing)
}
func testReportError() {
XCTAssertEqual(audioPlayerState.playbackState, .stopped)
@Test
mutating func reportError() {
#expect(audioPlayerState.playbackState == .stopped)
audioPlayerState.reportError()
XCTAssertEqual(audioPlayerState.playbackState, .error)
#expect(audioPlayerState.playbackState == .error)
}
func testUpdateProgress() async {
@Test
mutating func updateProgress() async {
audioPlayerState.attachAudioPlayer(audioPlayerMock)
// If we try to set a negative progress, the new progress must be 0.0
do {
await audioPlayerState.updateState(progress: -5.0)
XCTAssertEqual(audioPlayerState.progress, 0.0)
XCTAssertEqual(audioPlayerMock.seekToReceivedProgress, 0.0)
}
// If we try to set a progress > 1.0, the new progress must be 1.0
do {
await audioPlayerState.updateState(progress: 1.5)
XCTAssertEqual(audioPlayerState.progress, 1.0)
XCTAssertEqual(audioPlayerMock.seekToReceivedProgress, 1.0)
}
do {
audioPlayerMock.state = .stopped
await audioPlayerState.updateState(progress: 0.4)
XCTAssertEqual(audioPlayerState.progress, 0.4)
XCTAssertEqual(audioPlayerMock.seekToReceivedProgress, 0.4)
XCTAssertFalse(audioPlayerState.isPublishingProgress)
}
do {
audioPlayerMock.state = .playing
await audioPlayerState.updateState(progress: 0.4)
XCTAssertEqual(audioPlayerState.progress, 0.4)
XCTAssertEqual(audioPlayerMock.seekToReceivedProgress, 0.4)
XCTAssert(audioPlayerState.isPublishingProgress)
}
// If we try to set a negative progress, the new progress must be 0.0
await audioPlayerState.updateState(progress: -5.0)
#expect(audioPlayerState.progress == 0.0)
#expect(audioPlayerMock.seekToReceivedProgress == 0.0)
// If we try to set a progress > 1.0, the new progress must be 1.0
await audioPlayerState.updateState(progress: 1.5)
#expect(audioPlayerState.progress == 1.0)
#expect(audioPlayerMock.seekToReceivedProgress == 1.0)
audioPlayerMock.state = .stopped
await audioPlayerState.updateState(progress: 0.4)
#expect(audioPlayerState.progress == 0.4)
#expect(audioPlayerMock.seekToReceivedProgress == 0.4)
#expect(!audioPlayerState.isPublishingProgress)
audioPlayerMock.state = .playing
await audioPlayerState.updateState(progress: 0.4)
#expect(audioPlayerState.progress == 0.4)
#expect(audioPlayerMock.seekToReceivedProgress == 0.4)
#expect(audioPlayerState.isPublishingProgress)
}
func testHandlingAudioPlayerActionDidStartLoading() async throws {
@Test
func handlingAudioPlayerActionDidStartLoading() async throws {
audioPlayerState.attachAudioPlayer(audioPlayerMock)
let deferred = deferFulfillment(audioPlayerState.$playbackState) { action in
switch action {
case .loading:
@@ -157,15 +157,16 @@ class AudioPlayerStateTests: XCTestCase {
audioPlayerActionsSubject.send(.didStartLoading)
try await deferred.fulfill()
XCTAssertEqual(audioPlayerState.playbackState, .loading)
#expect(audioPlayerState.playbackState == .loading)
}
func testHandlingAudioPlayerActionDidFinishLoading() async throws {
@Test
mutating func handlingAudioPlayerActionDidFinishLoading() async throws {
audioPlayerMock.duration = 10.0
audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: 0)
audioPlayerState.attachAudioPlayer(audioPlayerMock)
let deferred = deferFulfillment(audioPlayerState.$playbackState) { action in
switch action {
case .readyToPlay:
@@ -179,15 +180,16 @@ class AudioPlayerStateTests: XCTestCase {
try await deferred.fulfill()
// The state is expected to be .readyToPlay
XCTAssertEqual(audioPlayerState.playbackState, .readyToPlay)
#expect(audioPlayerState.playbackState == .readyToPlay)
// The duration should have been updated with the player's duration
XCTAssertEqual(audioPlayerState.duration, audioPlayerMock.duration)
#expect(audioPlayerState.duration == audioPlayerMock.duration)
}
func testHandlingAudioPlayerActionDidStartPlaying() async throws {
@Test
mutating func handlingAudioPlayerActionDidStartPlaying() async throws {
await audioPlayerState.updateState(progress: 0.4)
audioPlayerState.attachAudioPlayer(audioPlayerMock)
let deferred = deferFulfillment(audioPlayerState.$playbackState) { action in
switch action {
case .playing:
@@ -199,16 +201,17 @@ class AudioPlayerStateTests: XCTestCase {
audioPlayerActionsSubject.send(.didStartPlaying)
try await deferred.fulfill()
XCTAssertEqual(audioPlayerMock.seekToReceivedProgress, 0.4)
XCTAssertEqual(audioPlayerState.playbackState, .playing)
XCTAssert(audioPlayerState.isPublishingProgress)
XCTAssert(audioPlayerState.showProgressIndicator)
#expect(audioPlayerMock.seekToReceivedProgress == 0.4)
#expect(audioPlayerState.playbackState == .playing)
#expect(audioPlayerState.isPublishingProgress)
#expect(audioPlayerState.showProgressIndicator)
}
func testHandlingAudioPlayerActionDidPausePlaying() async throws {
@Test
mutating func handlingAudioPlayerActionDidPausePlaying() async throws {
await audioPlayerState.updateState(progress: 0.4)
audioPlayerState.attachAudioPlayer(audioPlayerMock)
let deferred = deferFulfillment(audioPlayerState.$playbackState) { action in
switch action {
case .stopped:
@@ -220,16 +223,17 @@ class AudioPlayerStateTests: XCTestCase {
audioPlayerActionsSubject.send(.didPausePlaying)
try await deferred.fulfill()
XCTAssertEqual(audioPlayerState.playbackState, .stopped)
XCTAssertEqual(audioPlayerState.progress, 0.4)
XCTAssertFalse(audioPlayerState.isPublishingProgress)
XCTAssert(audioPlayerState.showProgressIndicator)
#expect(audioPlayerState.playbackState == .stopped)
#expect(audioPlayerState.progress == 0.4)
#expect(!audioPlayerState.isPublishingProgress)
#expect(audioPlayerState.showProgressIndicator)
}
func testHandlingAudioPlayerActionsidStopPlaying() async throws {
@Test
mutating func handlingAudioPlayerActionsidStopPlaying() async throws {
await audioPlayerState.updateState(progress: 0.4)
audioPlayerState.attachAudioPlayer(audioPlayerMock)
let deferred = deferFulfillment(audioPlayerState.$playbackState) { action in
switch action {
case .stopped:
@@ -241,16 +245,17 @@ class AudioPlayerStateTests: XCTestCase {
audioPlayerActionsSubject.send(.didStopPlaying)
try await deferred.fulfill()
XCTAssertEqual(audioPlayerState.playbackState, .stopped)
XCTAssertEqual(audioPlayerState.progress, 0.4)
XCTAssertFalse(audioPlayerState.isPublishingProgress)
XCTAssert(audioPlayerState.showProgressIndicator)
#expect(audioPlayerState.playbackState == .stopped)
#expect(audioPlayerState.progress == 0.4)
#expect(!audioPlayerState.isPublishingProgress)
#expect(audioPlayerState.showProgressIndicator)
}
func testAudioPlayerActionsDidFinishPlaying() async throws {
@Test
mutating func audioPlayerActionsDidFinishPlaying() async throws {
await audioPlayerState.updateState(progress: 0.4)
audioPlayerState.attachAudioPlayer(audioPlayerMock)
let deferred = deferFulfillment(audioPlayerState.$playbackState) { action in
switch action {
case .stopped:
@@ -262,16 +267,17 @@ class AudioPlayerStateTests: XCTestCase {
audioPlayerActionsSubject.send(.didFinishPlaying)
try await deferred.fulfill()
XCTAssertEqual(audioPlayerState.playbackState, .stopped)
#expect(audioPlayerState.playbackState == .stopped)
// Progress should be reset to 0
XCTAssertEqual(audioPlayerState.progress, 0.0)
XCTAssertFalse(audioPlayerState.isPublishingProgress)
XCTAssertFalse(audioPlayerState.showProgressIndicator)
#expect(audioPlayerState.progress == 0.0)
#expect(!audioPlayerState.isPublishingProgress)
#expect(!audioPlayerState.showProgressIndicator)
}
func testAudioPlayerActionsDidFailed() async throws {
@Test
func audioPlayerActionsDidFailed() async throws {
audioPlayerState.attachAudioPlayer(audioPlayerMock)
let deferredPlayingState = deferFulfillment(audioPlayerState.$playbackState) { action in
switch action {
case .playing:
@@ -282,8 +288,8 @@ class AudioPlayerStateTests: XCTestCase {
}
audioPlayerActionsSubject.send(.didStartPlaying)
try await deferredPlayingState.fulfill()
XCTAssertFalse(audioPlayerState.showProgressIndicator)
#expect(!audioPlayerState.showProgressIndicator)
let deferred = deferFulfillment(audioPlayerState.$playbackState) { action in
switch action {
case .error:
@@ -295,8 +301,8 @@ class AudioPlayerStateTests: XCTestCase {
audioPlayerActionsSubject.send(.didFailWithError(error: AudioPlayerError.genericError))
try await deferred.fulfill()
XCTAssertEqual(audioPlayerState.playbackState, .error)
XCTAssertFalse(audioPlayerState.isPublishingProgress)
XCTAssertFalse(audioPlayerState.showProgressIndicator)
#expect(audioPlayerState.playbackState == .error)
#expect(!audioPlayerState.isPublishingProgress)
#expect(!audioPlayerState.showProgressIndicator)
}
}