Pruebas en Swift: unit, integration, e2e — nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Pruebas avanzadas en Swift: Unit, Integration y E2E



Las pruebas no son solo una verificación, sino la base de un código confiable. En un nivel avanzado, el desarrollador de Swift no solo necesita escribir pruebas, sino construir correctamente la pirámide de pruebas: desde pruebas unitarias rápidas hasta complejos escenarios end-to-end (e2e). En este artículo, analizaremos técnicas avanzadas para proyectos iOS/macOS: trabajo con dependencias, pruebas de integración con CoreData y automatización de UI.



1. Pruebas unitarias: gestión de dependencias y mocks



En un nivel avanzado, las pruebas unitarias deben estar aisladas. La técnica clave es la Inyección de Dependencias (DI). En lugar de probar la capa de red real o la base de datos, las reemplazamos con mocks.



Ejemplo: protocolo y DI

protocol NetworkServiceProtocol {    func fetchData(completion: @escaping (Result<String, Error>) -> Void)}

class ViewModel { private let networkService: NetworkServiceProtocol init(networkService: NetworkServiceProtocol) { self.networkService = networkService } func loadData() { networkService.fetchData { result in // procesamiento } }}

// Mock para pruebasclass MockNetworkService: NetworkServiceProtocol { var shouldReturnError = false func fetchData(completion: @escaping (Result<String, Error>) -> Void) { if shouldReturnError { completion(.failure(NSError(domain: "test", code: -1))) } else { completion(.success("Mock data")) } }}


En la prueba, creas un MockNetworkService y lo pasas al ViewModel. Esto permite verificar escenarios de error y éxito sin una API real.



XCTestExpectation para asincronía

func testViewModelLoadDataSuccess() {    let mock = MockNetworkService()    let viewModel = ViewModel(networkService: mock)        let expectation = self.expectation(description: "Data loaded")        viewModel.loadData()        // Simulamos la llamada asíncrona    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {        expectation.fulfill()    }        waitForExpectations(timeout: 1.0)    // Verificamos el estado del ViewModel}


2. Pruebas de integración: CoreData y trabajo con datos reales



Las pruebas de integración verifican la interacción entre componentes. Para CoreData, usamos un contenedor en memoria para no contaminar la base de datos principal.



Configuración de CoreData en memoria

class CoreDataTestStack {    lazy var persistentContainer: NSPersistentContainer = {        let container = NSPersistentContainer(name: "MyModel")        let description = NSPersistentStoreDescription()        description.type = NSInMemoryStoreType        container.persistentStoreDescriptions = [description]        container.loadPersistentStores { _, error in            if let error = error {                fatalError("Failed to load store: \\(error)")            }        }        return container    }()        var context: NSManagedObjectContext {        return persistentContainer.viewContext    }}


Ejemplo de prueba de integración

func testSaveAndFetchUser() {    let stack = CoreDataTestStack()    let repository = UserRepository(context: stack.context)        // Creamos un usuario    let user = User(context: stack.context)    user.name = "Test User"    user.age = 30        try? stack.context.save()        // Verificamos que los datos se hayan guardado    let fetchRequest: NSFetchRequest<User> = User.fetchRequest()    let users = try? stack.context.fetch(fetchRequest)        XCTAssertEqual(users?.count, 1)    XCTAssertEqual(users?.first?.name, "Test User")}


Importante: después de cada prueba, limpia el contexto para que las pruebas no se afecten entre sí.

Blogs

Book Recommendations