[TDD] SessionManager Without Knowing the Storage Type

Muhammad Alfiansyah
2 min readFeb 6, 2025

--

Instead of hardcoding UserDefaults or Keychain, we create a protocol that defines session storage behavior.

struct User: Codable {
let id: String
let name: String
let email: String
}

protocol SessionStorage {
func saveUser(_ user: User)
func loadUser() -> User?
func clearUser()
}
  • SessionManager doesn’t depend on UserDefaults/Keychain directly.
  • We can swap storage solutions easily later.
  • We can mock the storage for unit testing.

Now, SessionManager doesn’t depend on UserDefaults directly. Instead, it uses any session storage that conforms to SessionStorage.

class SessionManager {
private let storage: SessionStorage

init(storage: SessionStorage) {
self.storage = storage
}

func saveUser(_ user: User) {
storage.saveUser(user)
}

func loadUser() -> User? {
return storage.loadUser()
}

func clearUser() {
storage.clearUser()
}
}
  • SessionManager doesn’t care whether storage is UserDefaults, Keychain, or something else.
  • It’s testable because we can inject a mock storage for testing.

To test SessionManager, we mock storage instead of using real UserDefaults.

class MockSessionStorage: SessionStorage {
var storedUser: User?

func saveUser(_ user: User) {
storedUser = user
}

func loadUser() -> User? {
return storedUser
}

func clearUser() {
storedUser = nil
}
}
  • We avoid real data storage during testing.
  • We can manipulate test data easily.

Now, we test SessionManager with MockSessionStorage instead of real storage.

import XCTest

class SessionManagerTests: XCTestCase {
var sessionManager: SessionManager!
var mockStorage: MockSessionStorage!

override func setUp() {
super.setUp()
mockStorage = MockSessionStorage()
sessionManager = SessionManager(storage: mockStorage)
}

func testSaveAndLoadUser() {
let testUser = User(id: "123", name: "John Doe", email: "john@example.com")
sessionManager.saveUser(testUser)

let loadedUser = sessionManager.loadUser()
XCTAssertNotNil(loadedUser, "User should be saved and loaded")
XCTAssertEqual(loadedUser?.id, testUser.id, "User ID should match")
XCTAssertEqual(loadedUser?.name, testUser.name, "User name should match")
XCTAssertEqual(loadedUser?.email, testUser.email, "User email should match")
}

func testClearUser() {
sessionManager.saveUser(User(id: "123", name: "John Doe", email: "john@example.com"))
sessionManager.clearUser()

let loadedUser = sessionManager.loadUser()
XCTAssertNil(loadedUser, "User should be cleared")
}
}
  • MockSessionStorage mimics real storage but doesn’t use UserDefaults.
  • We test saving, loading, and clearing a user.
  • Tests run fast and reliably without modifying actual user data.

--

--

No responses yet