Post

XCTestXCTAssertThrowsErrorEqual

Releyendo el libro Diseño Ágil con TDD de Carlos Blé Jurado (que recomiendo ampliamente) y practicando los ejercicios que incluye, he hecho esta extensión de XCTest que es función de test que evalúa 3 cosas:

  • que se lance un error
  • que el error sea del tipo especificado
  • que sea de un caso en específico

1) La cabecera

Primero debemos definir el genérico, en este caso esperamos un Error y que sea Equatable para poder compararlo.

El autoclosure es una técnica que permite esperar a evaluar una expresión hasta el momento en que lo necesitemos.

En este ejemplo, la función es isStrongPassword por que lo expression devuelve un Bool, y necesitamos que espere hasta que sea evaluada por  XCTAssertThrowsError

De la que esperamos que lance un error y que sea Equatable para poder hacer los assertions.

Los parámetros file y line permiten saber en que archivo y linea se ha producido el error así que asignamos ese valor por defecto #file y #line.

2)

Esperamos un error  y un tipo de error, opcionales (si se llega a producir). Evaluaremos con XCTAssertThrowsError y les asignaremos el error que ha producido y su tipo.

3)

Validamos que efectivamente lance un error expectedError, nuestro propio error, en este caso PasswordError

4)

y además que coincidan el error indicado en la cabecera (expectedError) con el error lanzado por expression

De esta manera al escribir los tests el código es más legible y menos farragoso al tener que evaluar cada caso con XCTAssertThrowsError y en el closure que nos habilita.

Os comparto el código y cualquier mejora es bien recibida.

func XCTAssertThrowsErrorEqual<E: Error & Equatable>(_ expression: @autoclosure () throws -> any Equatable, 
                                                         _ expectedError: E,
                                                         file: StaticString = #file,
                                                         line: UInt = #line) {
        var thrownError: Error?
        var errorType: (any Error.Type)?
        XCTAssertThrowsError(try expression()) { error in
            thrownError = error
            errorType = type(of: error)
        }
        XCTAssertTrue(thrownError is E,
                      "Unexpected error type: \(String(describing: errorType)) insted of \(E.Type.self)", file: file, line: line)
        if let unwrappedThrownError = thrownError as? E {
            XCTAssertEqual(unwrappedThrownError, expectedError, 
                           "Error was not equal to the expected error", file: file, line: line)
        }
    }