Swift Unit Testing

Standard

XCode y Swift nos ofrecen un framework para realizar las Pruebas Unitarias (Unit Testing) de código y de UI. En este post les voy a compartir algunos ejemplos de pruebas que se pueden diseñar para sus proyectos.

39FE698E-527D-4BCB-A1E5-FB4A9136D82E

Control de Accesos

A diferencia de otras tecnologías similares, las aplicaciones Swift ofrece un manejo por separado de los Targets en los cuales puedes ver tus clases. Esto causaba problemas, pero en la versión 7 de XCode ya hay una opción más elegante de evitar lidiar con los Target Membershipts.

D6D529E4-0126-43EC-9804-D6482A97E885

Ahora tenemos la opción más sencilla de usar la @testable.

@testable import SimpleTest

Esta opción,  estaremos usando en esta publicación.

Clase de Prueba

Para las pruebas tomaremos “prestada” la siguiente clase Quotes:

https://github.com/NatashaTheRobot/iOS8DynamicTypeDemo/blob/master/iOS8DynamicTypeDemo/Quote.swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Quote {
    var content: String
    var scenario: String
 
    class func allQuotes() -> [Quote] {
        let quotes = [
            Quote(content: "Sometimes the road less traveled is less traveled for a reason.", scenario: "Jerry in The Baby Shower"),
            Quote(content: "I can't go to a bad movie by myself. What, am I gonna make sarcastic remarks to strangers?", scenario: "Jerry in The Chinese Restaurant"),
            Quote(content: "You know the message you're sending out to the world with these sweatpants? You're telling the world, 'I give up. I can't compete in normal society. I'm miserable, so I might as well be comfortable.", scenario: "Jerry in The Pilot"),
            Quote(content: "Man, it's the nineties... It's Hammer time!", scenario: "Kramer in The Parking Space"),
            Quote(content: "Why is nice bad? What kind of a sick society are we living in when nice is bad?", scenario: "George in The Cafe"),
            Quote(content: "When you look annoyed all the time, people think that you're busy.", scenario: "George in The Hot Tub"),
            Quote(content: "I would drape myself in velvet if it were socially acceptable.", scenario: "George in The Label Maker"),
            Quote(content: "You know what they say, 'You don't sell the steak, you sell the sizzle.'", scenario: "Kramer in The Bizzaro Jerry"),
            Quote(content: "直往前走!左/右拐弯到直往前走左/右拐弯到直往前走!左/右拐弯到!直往前走!左/右拐弯到", scenario: "Jerry learning Chinese")
        ]
        return quotes
    }
    
    init(content: String, scenario: String) {
        self.content = content
        self.scenario = scenario
    }
}

Caso de Prueba 1: Inicialización de Objetos

Hay métodos para el ciclo de vida del caso de prueba como setup y teardown, o también puedes definir tus variables de manera directa y usarlas en tus pruebas.

Tomando como ejemplo el método Quotes.allQuotes() que regresa un arreglo de objetos Quote, vamos a usar una variable allQuotes para nuestras pruebas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import XCTest
@testable import SimpleTest
 
class QuoteTests: XCTestCase {
    var allQuotes = Quote.allQuotes()
    func testCCount() {
        let count = allQuotes.count
        XCTAssertEqual(count, 9)
    }
 
    func testBAdd() {
        let quote = Quote(content: "hello, world", scenario: "minions")
        allQuotes.append(quote)
        XCTAssertEqual(allQuotes.count, 10)
    }
    
    func testARemoveAll() {
        allQuotes.removeAll()
        XCTAssertEqual(allQuotes.count, 0)
    }
}

 Las pruebas se ejecutan en orden alfabético, y dado que la inicialización de la variable allQuotes se reutiliza para cada prueba de mi caso de prueba, el borrar por ejemplo la prueba testBAdd(), no impacta el número de Quotes que se validan en mis pruebas.

Caso de Prueba 2: Constantes y Variables

En este caso de pruebas iniciamos el campo content de la constante quote, y estamos probando las diferentes cuando modificamos el valor de content. Podemos ver en el caso de prueba que quote es el mismo para cada caso.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import XCTest
@testable import SimpleTest
 
class ContentTests : XCTestCase {
    let quote = Quote(content: "hello", scenario: "world")
 
    func testModified() {
        quote.content = "OmarPS"
        XCTAssertEqual(quote.content, "OmarPS")
    }
    func testNotModified() {
        XCTAssertEqual(quote.content, "hello")
    }
}

Caso de Prueba: Unwrap Optional

En el caso de los optionals, aunque es buena idea validarlos durante el desarrollo, ejemplo caso testFail(), en el caso de las pruebas me interesa que marque error si mi variable no está inicializada correctamente. Por lo que definitivamente es mejor usar el enfoque del caso testOk().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import XCTest
 
class OptionalTests: XCTestCase {
    // Falla porque no se valida el valor del optional
    func testFail() {
        var optional: String?
        if let opt = optional {
            XCTAssertEqual(opt, "fail")
        } else {
            XCTFail()
        }
    }
 
    // Manera correcta de probar el valor de un optional
    func testOk() {
        let optional: String? = "OmarPS"
        XCTAssertEqual(optional!, "OmarPS")
    }
}

Comentarios Finales

Les recomiendo que empiecen a implementar estas Suites de Pruebas en su clases y apps, para aumentar la estabilidad de sus bases de código conforme empiezan a crecer sus proyectos.

Swift y XCode ofrecen herramientas que nos ayudan en la implementación de pruebas de código, pruebas de UI e Integración Continua, entre otras.

En Hunabsys estamos implementando varias iniciativas siguiendo estas líneas en las diferentes tecnologías que manejamos. Más adelante compartiré nuevos posts al respecto.

Leave a Reply

Your email address will not be published. Required fields are marked *