Mocking to Test API call

--

To test API calls in XCTest, you can use mocking techniques to provide fake data and simulate the server’s responses without actually making real API requests. This ensures that your tests are isolated from external dependencies and behave consistently. In Swift, one common approach for mocking is using dependency injection, where you inject a mock networking service into your code during testing. Here’s a step-by-step guide on how to do this:

  1. Create a protocol for your networking service:
protocol NetworkingService {
func fetchData(url: URL, completion: @escaping (Data?, Error?) -> Void)
}
  1. Implement your mock networking service conforming to the NetworkingService protocol:
class MockNetworkingService: NetworkingService {
func fetchData(url: URL, completion: @escaping (Data?, Error?) -> Void) {
// Mock the data and response here (e.g., create a sample JSON response)
let jsonString = """
{
"title": "Sample Movie",
"genre": "Drama"
}
"""
if let data = jsonString.data(using: .utf8) {
completion(data, nil)
} else {
let error = NSError(domain: "MockNetworkingServiceErrorDomain", code: -1, userInfo: nil)
completion(nil, error)
}
}
}
  1. In your API client or manager class, use the NetworkingService protocol instead of directly using URLSession or Alamofire. For example:
class MovieAPIClient {
private let networkingService: NetworkingService

init(networkingService: NetworkingService) {
self.networkingService = networkingService
}
func fetchMovieData(completion: @escaping (Result<Movie, Error>) -> Void) {
guard let url = URL(string: "https://api.example.com/movies") else {
return
}
networkingService.fetchData(url: url) { data, error in
if let error = error {
completion(.failure(error))
return
}
// Parse the data into your Movie model or any other model you are using
// For simplicity, let's assume we have a Movie struct:
let decoder = JSONDecoder()
do {
let movie = try decoder.decode(Movie.self, from: data!)
completion(.success(movie))
} catch {
completion(.failure(error))
}
}
}
}
  1. Write XCTest cases to test your API calls using the mock networking service:
import XCTest
@testable import YourApp

class MovieAPIClientTests: XCTestCase {
var apiClient: MovieAPIClient!
override func setUp() {
super.setUp()
// Use the mock networking service during testing
let mockNetworkingService = MockNetworkingService()
apiClient = MovieAPIClient(networkingService: mockNetworkingService)
}
override func tearDown() {
apiClient = nil
super.tearDown()
}
func testFetchMovieData() {
let expectation = self.expectation(description: "Fetch movie data")
apiClient.fetchMovieData { result in
switch result {
case .success(let movie):
// Assert the properties of the movie object you expect to receive
XCTAssertEqual(movie.title, "Sample Movie")
XCTAssertEqual(movie.genre, "Drama")
case .failure(let error):
XCTFail("Error: \(error.localizedDescription)")
}
expectation.fulfill()
}
wait(for: [expectation], timeout: 5.0)
}
}

In this example, we’re testing the MovieAPIClient using a mock networking service (MockNetworkingService). During the test, the API client will use the mock networking service to fetch data instead of making real API calls. The test then asserts that the movie data received from the API client matches the expected values.

By using mocking techniques, you can create controlled test environments and ensure the correctness of your code even when the actual API is unavailable or behaves inconsistently.

Please like and follow if you really like my article, it motivates me to continue….

--

--

Responses (4)