Swift Testing (Parte 4 de 5)1

Introducción a Swift Testing

2

Organiza Pruebas con Swift Testing usando Suites y Tags

3

Evaluando pruebas con Swift Testing

4

Pruebas unitarias asíncronas con Swift Testing

5

Deshabilitando y Habilitando pruebas con Swift Testing

Pruebas unitarias asíncronas con Swift Testing

Es necesario utilizar Xcode 16 Beta o superior para trabajar con Swift Testing.

En el artículo anterior de esta serie vimos cómo evaluar pruebas usando la macro #expect. Sin embargo, esta macro tiene una limitante y es que no nos facilita crear pruebas para código que utiliza completion blocks. Para realizar este tipo de pruebas utilizamos confirmation(_:expectedCount:sourceLocation:_:).

Para validar que un evento ocurre cero o más veces en XCTest, podíamos lograr esto usando XCTestExpectation. En este artículo veremos cómo podemos realizar esto usando confirmation.

Para esto necesitaremos utilizar un código de base al cual iremos agregando pruebas unitarias:

protocol Provider {
func getNotes() async -> [String]
func getNotes(completion: ([String]) -> Void)
}

Este protocolo define los requerimientos de nuestros proveedores de información. Tenemos dos métodos que implementar, ambos llamados getNotes, pero uno es async y el otro recibe un completion. Ambos métodos retornan un arreglo de String.

Ahora las implementaciones:

struct LiveProvider: Provider {
func getNotes(completion: ([String]) -> Void) {
}
func getNotes() async -> [String] {
[]
}
}
struct MockProvider: Provider {
func getNotes(completion: ([String]) -> Void) {
completion(["Note 1", "Note 2"])
}
func getNotes() async -> [String] {
["Note 1", "Note 2"]
}
}

Ahora tenemos dos struct que conforman a Provider. La primera, LiveProvider, es solo una implementación vacía y sería el proveedor de información que usaríamos en producción. Mientras que MockProvider es un mock que simula las respuestas, ideal para pruebas, ya que no requiere llamadas a un servidor.

Por último, necesitaremos una modificación a NotesManager, una clase que ya utilizamos en un artículo anterior, Evaluando pruebas con Swift Testing.

final class NotesManager {
private let provider: Provider
private(set) var notes = [String]()
// 1
init(provider: Provider = LiveProvider()) {
self.provider = provider
}
// 2
func getRemoteNotes() async -> [String] {
return await provider.getNotes()
}
// 3
func getRemoteNotes(completion: @escaping ([String]) -> Void) {
provider.getNotes(completion: completion)
}
}

¿Qué hace este código?

  1. En el constructor esperamos un Provider, y por defecto pasamos el LiveProvider. Esto nos permitirá hacer inyección de dependencias y así poder utilizar diferentes objetos dependiendo del caso.
  2. La primera implementación de getRemoteNotes es async y llama directamente a provider.getNotes(), retornando el resultado.
  3. La segunda implementación de getRemoteNotes espera un completion y ejecuta provider.getNotes(completion: completion).

Este es el código para el cual escribiremos las pruebas unitarias.

Escribiendo pruebas unitarias asíncronas

Podemos crear un nuevo archivo File > New > File from Template, seleccionamos Swift Testing Unit Test e indicamos el nombre NoteManagerTests.swift.

Crear un archivo para pruebas unitarias

Borramos todo el contenido del archivo y agregamos lo siguiente:

// 1
import Testing
@testable import MyApp
@Suite
struct NotesManagerTests {
// 2
private let manager = NotesManager(provider: MockProvider())
// 4
@Test func getRemoteNotes() async {
let notes = await manager.getRemoteNotes()
#expect(["Note 1", "Note 2"] == notes)
}
}

Este código:

  1. Importa Testing para poder utilizar la librería.
  2. Instanciamos un NotesManager pasando en provider el mock MockProvider().
  3. Creamos la prueba unitaria getRemoteNotes(), la cual es async y verificamos que getRemoteNotes() retorna los datos esperados ["Note 1", "Note 2"].

Evaluando pruebas unitarias usando confirmation

Hasta ahora, esta prueba es bastante sencilla y no se diferencia de lo que hemos visto en artículos anteriores. Sin embargo, ¿qué pasa si necesitamos probar la implementación que recibe un completion?

Justo debajo de getRemoteNotes(), agregamos la siguiente prueba unitaria:

// 1
@Test func getRemoteNotesWithCompletion() async {
// 2
await confirmation("Service called one time") { operation in
manager.getRemoteNotes { _ in
operation()
}
}
}

En este código:

  1. Creamos otra prueba llamada getRemoteNotesWithCompletion, que también es async, porque confirmation(_:expectedCount:sourceLocation:_:) es una operación asíncrona.
  2. Usando confirmation, pasamos un comentario como primer parámetro, el cual se mostrará en los resultados de las pruebas. Luego pasamos un closure, el cual nos provee una variable que hemos ejecutado operation. Hacemos la llamada a getRemoteNotes(completion:) y dentro del closure ejecutamos operation(). Esto incrementará el conteo de llamadas esperadas por confirmation.

Por defecto, el número de llamadas esperadas es uno, es decir, que esta prueba espera que operation() se ejecute una sola vez. Podemos cambiar el número de veces esperadas utilizando el parámetro expectedCount. Por ejemplo, si quisiéramos que fueran dos en total:

await confirmation("Service called one time", expectedCount: 2) {
...
}

Ejecuta todas las pruebas usando Command + U, para verificar que todas se ejecutan exitosamente. ¡Buen trabajo!

Conclusión

confirmation es una de las piezas que hace Swift Testing aún más completo. Nos provee una manera fácil de lidiar con pruebas que requieren ejecutar completion blocks. Este tipo de código es muy común, especialmente en código legado, en aplicaciones donde async/await no se utiliza o en casos donde no hayamos completado la migración a concurrencia moderna.

Ahora contamos con otra herramienta gracias a confirmation. Todavía nos quedan cosas que explorar sobre Swift Testing, ¡hasta la próxima!

Swift Testing (Parte 4 de 5)1

Introducción a Swift Testing

2

Organiza Pruebas con Swift Testing usando Suites y Tags

3

Evaluando pruebas con Swift Testing

4

Pruebas unitarias asíncronas con Swift Testing

5

Deshabilitando y Habilitando pruebas con Swift Testing

Comparte este artículo

Subscríbete a nuestro Newsletter

Mantente al día en el mundo de las aplicaciones móviles con nuestro blog especializado.

Artículos semanales

Todas las semanas artículos nuevos sobre el mundo de las aplicaciones móviles.

No spam

No te enviaremos spam, solo contenido de calidad. Puedes darte de baja cuando quieras.

Contenido de calidad

Nada de contenido generado de manera automática usando ChatGPT.

Recomendaciones

Tips indispensables sobre mejores prácticas y metodologías.

© 2024 AsyncLearn