“Imperfect tests, run frequently, are much better than perfect tests that are never written at all” — Martin Fowler
Writing testable code is a crucial skill for any iOS developer. It helps you catch bugs early, refactor safely, and build robust applications. In this article, I'll share some best practices and patterns for writing testable code in iOS.
1. Separate Concerns
Keep your business logic separate from your UI code. Use patterns like MVC, MVVM, or Clean Architecture to achieve this separation.
2. Use Dependency Injection
Inject dependencies (like network clients, data stores) instead of hardcoding them. This makes it easy to swap real implementations with mocks or stubs in tests.
class UserService {
let apiClient: APIClient
init(apiClient: APIClient) {
self.apiClient = apiClient
}
}
3. Favor Protocols Over Concrete Types
Define protocols for your dependencies. This allows you to mock them in tests.
protocol APIClient {
func fetchData(completion: (Result) -> Void)
}
4. Minimize Global State
Global state makes tests unpredictable. Avoid singletons unless necessary, and prefer passing dependencies explicitly.
5. Write Small, Focused Functions
Small functions are easier to test and reason about. Each function should do one thing well.
6. Use Test Doubles
Use mocks, stubs, and fakes to simulate dependencies in your tests.
class MockAPIClient: APIClient {
func fetchData(completion: (Result) -> Void) {
// Return mock data
completion(.success(Data()))
}
}
7. Test ViewModels and Business Logic
Focus your tests on ViewModels and business logic, not UI code. UI tests are important but can be brittle and slow.
8. Use XCTest
Leverage XCTest for unit and UI testing in iOS. Write tests that are fast, reliable, and easy to maintain.
import XCTest
class UserServiceTests: XCTestCase {
func testFetchUserSuccess() {
let mockClient = MockAPIClient()
let service = UserService(apiClient: mockClient)
// ... test logic ...
}
}
Conclusion
Writing testable code is an investment that pays off in the long run. By following these practices, you'll build apps that are easier to maintain, refactor, and scale.