Pruebas Unitarias para Servicios de Base de Datos en Swift

Clase 9 de 15Curso de Swift Unit Testing

Resumen

La implementación de pruebas unitarias en el desarrollo de aplicaciones es fundamental para garantizar la calidad y robustez del código. Cuando trabajamos con bases de datos en Swift, es crucial verificar que nuestras funciones de acceso a datos funcionen correctamente en todo momento. En este artículo, exploraremos cómo aislar y probar un servicio de base de datos en una aplicación Swift, asegurando que todas las operaciones CRUD (Crear, Leer, Actualizar, Eliminar) se ejecuten correctamente.

¿Cómo aislar y probar un servicio de base de datos en Swift?

Cuando desarrollamos aplicaciones que interactúan con bases de datos, es esencial verificar que nuestro servicio de acceso a datos funcione correctamente. En la clase anterior, integramos nuestro view model con la base de datos y realizamos pruebas de integración. Ahora, vamos a aislar nuestro servicio de base de datos para probarlo de manera independiente.

El objetivo es garantizar que todas las funciones de nuestro servicio de base de datos real estén correctamente implementadas. Para esto, crearemos pruebas unitarias específicas para cada operación que realiza nuestro servicio.

¿Cómo configurar el entorno de pruebas para el servicio de base de datos?

El primer paso es crear un nuevo archivo de prueba unitaria para nuestro servicio de base de datos. Seguiremos estos pasos:

  1. En nuestro target de test, creamos un nuevo archivo desde template.
  2. Seleccionamos "Unit Test" y lo nombramos "SDDatabaseServiceTest".
  3. Guardamos el archivo en la carpeta correspondiente.

Una vez creado el archivo, debemos configurar el entorno de pruebas:

import XCTest
@testable import Gastify

class SDDatabaseServiceTest: XCTestCase {
    var databaseService: SDDatabaseService!
    
    override func setUp() {
        super.setUp()
        let expectation = expectation(description: "Setup")
        Task { @MainActor in
            databaseService = SDDatabaseService()
            await clearDatabase()
            expectation.fulfill()
        }
        wait(for: [expectation], timeout: 1.0)
    }
    
    override func tearDown() {
        super.tearDown()
        clearDatabase()
    }
    
    private func clearDatabase() {
        // Función para limpiar la base de datos
    }
}

En el método setUp(), inicializamos nuestro servicio de base de datos y limpiamos cualquier dato existente para asegurar que nuestras pruebas comiencen con una base de datos vacía. Utilizamos un expectation para manejar la naturaleza asíncrona de estas operaciones.

¿Cómo probar la función de guardar un nuevo registro?

La primera función que probaremos es la de agregar un nuevo registro a la base de datos:

func testSaveNewRecord() async {
    let record = Record(id: UUID(), title: "Test", amount: 100, date: Date(), type: .expense)
    
    let success = await databaseService.saveNewRecord(record)
    
    XCTAssertTrue(success, "El registro debería guardarse correctamente")
    
    let fetchedRecords = await databaseService.fetchRecords(for: .today)
    
    XCTAssertTrue(fetchedRecords.contains(where: { $0.id == record.id }), "El registro guardado debería encontrarse en la base de datos")
}

En esta prueba:

  1. Creamos un registro de prueba con datos aleatorios.
  2. Llamamos a la función saveNewRecord de nuestro servicio.
  3. Verificamos que la función devuelva true, indicando que el registro se guardó correctamente.
  4. Recuperamos los registros de hoy de la base de datos.
  5. Verificamos que el registro que acabamos de guardar esté presente en los resultados.

¿Cómo probar la función de recuperar registros?

A continuación, probamos la función que recupera registros de la base de datos:

func testFetchRecords() async {
    let record = Record(id: UUID(), title: "Test", amount: 100, date: Date(), type: .expense)
    
    _ = await databaseService.saveNewRecord(record)
    
    let fetchedRecords = await databaseService.fetchRecords(for: .today)
    
    XCTAssertGreaterThan(fetchedRecords.count, 0, "Debería haber al menos un registro en la base de datos")
}

En esta prueba:

  1. Creamos y guardamos un registro de prueba.
  2. Llamamos a la función fetchRecords para recuperar los registros de hoy.
  3. Verificamos que la lista de registros recuperados tenga al menos un elemento.

¿Qué otras funciones debemos probar en nuestro servicio de base de datos?

Además de las funciones para guardar y recuperar registros, es importante probar otras operaciones clave de nuestro servicio de base de datos:

  1. Actualizar un registro existente: Verificar que podemos modificar un registro ya guardado en la base de datos.
  2. Eliminar un registro existente: Comprobar que podemos eliminar correctamente un registro de la base de datos.
  3. Obtener totales: Validar que las funciones que calculan totales (como gastos totales, ingresos totales, etc.) funcionan correctamente.

Para implementar estas pruebas, seguiríamos un enfoque similar al que utilizamos para las funciones de guardar y recuperar registros:

func testUpdateExistingRecord() async {
    // Implementación pendiente
}

func testDeleteExistingRecord() async {
    // Implementación pendiente
}

func testGetTotals() async {
    // Implementación pendiente
}

Es importante que cada prueba sea independiente y verifique una única funcionalidad específica. Además, debemos asegurarnos de que el entorno de pruebas se reinicie correctamente entre pruebas para evitar interferencias.

Las pruebas unitarias son una parte fundamental del desarrollo de software de calidad. Al aislar y probar nuestro servicio de base de datos, podemos tener la confianza de que nuestras operaciones de datos funcionarán correctamente en producción. ¿Has implementado pruebas unitarias en tus proyectos Swift? Comparte tu experiencia en los comentarios.