[TDD] SessionManager Without Knowing the Storage Type
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.