Cómo probar unitariamente métodos privados en Swift

Descargue su copia gratuita de
El manual que falta
para el desarrollo en Swift

La guía que desearía haber tenido cuando empecé

Únase a 20,000+ Desarrolladores que aprenden sobre el desarrollo en Swift

Descargue su copia gratuita

Probar por unidades un proyecto en Swift es bastante diferente a probar por unidades un proyecto escrito en Objective-C. Para aquellos que están acostumbrados a la flexibilidad del tiempo de ejecución de Objective-C, puede sentirse como si sus manos estuvieran atadas a su espalda.

Control de acceso

Aunque el control de acceso es una adición muy bienvenida con muchos beneficios, puede complicar las pruebas unitarias, especialmente si usted es nuevo en las pruebas unitarias. Usted probablemente sabe que usted puede aplicar el atributo testable a una declaración de importación en un objetivo de prueba para obtener acceso a las entidades que se declaran como internas.

import XCTest@testable import Notesclass NotesTests: XCTestCase { ...}

Aunque esto es una adición conveniente, no le da acceso a las entidades privadas en un objetivo de prueba. Esto nos lleva a la pregunta del día. ¿Cómo se prueban las entidades privadas?

Pregunta equivocada

La respuesta corta a esta pregunta es simple. No puedes acceder a las entidades privadas desde otro módulo y esto también se aplica a los objetivos de prueba. Simple y llanamente. Para eso está el control de acceso.

Pero esa no es la respuesta a la pregunta. Si preguntas cómo probar unitariamente entidades privadas, entonces estás haciendo la pregunta equivocada. Pero, ¿por qué?

No es asunto tuyo

¿Por qué declaras una entidad privada? Cuál es su motivación para hacerlo? Echa un vistazo al siguiente ejemplo.

import Foundationstruct AccountViewViewModel { // MARK: - Properties let account: Account // MARK: - Public Interface var subscriptionAsString: String { switch account.subscription { case .monthly: return "Monthly Subscription" case .yearly: return "Yearly Subscription" case .trial: return "Trial Subscription" } } var expiresAtAsString: String { // Parse Date let date = parse(date: account.expiresAt) // Initialize Date Formatter let dateFormatter = DateFormatter() // Configure Date Formatter dateFormatter.dateFormat = "YYYY, MMMM" // Convert Date to String return dateFormatter.string(from: date) } // MARK: - Private Interface private func parse(date dateAsString: String) -> Date { let dateFormat: String if dateAsString.contains("/") { dateFormat = "YYYY'/'MM'/'dd" } else { dateFormat = "YYYYMMdd" } // Initialize Date Formatter let dateFormatter = DateFormatter() // Configure Date Formatter dateFormatter.dateFormat = dateFormat if let date = dateFormatter.date(from: dateAsString) { print(date) return date } else { fatalError("Incompatible Date Format") } }}

Me gustaría probar unitariamente la estructura AccountViewViewModel. Como puede ver, la estructura AccountViewViewModel expone dos propiedades computadas internas y también define un método privado. La propiedad computada expiresAtAsString descarga parte de su trabajo al método privado parse(date:). Probar las propiedades computadas internas es sencillo.

// MARK: - Tests for Subscription as Stringfunc testSubscriptionAsString_Monthly() { let account = Account(expiresAt: "20161225", subscription: .monthly) let accountViewViewModel = AccountViewViewModel(account: account) XCTAssertEqual(accountViewViewModel.subscriptionAsString, "Monthly Subscription")}func testSubscriptionAsString_Yearly() { let account = Account(expiresAt: "20161225", subscription: .yearly) let accountViewViewModel = AccountViewViewModel(account: account) XCTAssertEqual(accountViewViewModel.subscriptionAsString, "Yearly Subscription")}func testSubscriptionAsString_Trial() { let account = Account(expiresAt: "20161225", subscription: .trial) let accountViewViewModel = AccountViewViewModel(account: account) XCTAssertEqual(accountViewViewModel.subscriptionAsString, "Trial Subscription")}// MARK: - Tests for Expires at as Stringfunc testExpiresAtAsString_20161225() { let account = Account(expiresAt: "20161225", subscription: .trial) let accountViewViewModel = AccountViewViewModel(account: account) XCTAssertEqual(accountViewViewModel.expiresAtAsString, "2016, December")}

¿Pero cómo probamos el método privado? No podemos acceder al método privado desde el objetivo de la prueba. Pero, ¿por qué deberíamos probar unitariamente el método privado? Por algo lo hemos marcado como privado. ¿No es así? Y eso nos lleva a la respuesta a la pregunta con la que empezamos. No probamos los métodos privados.

Testando unitariamente la interfaz pública

Al probar unitariamente la interfaz pública de la AccountViewViewModelestructura automáticamente o implícitamente probamos unitariamente la interfaz privada de la estructura. Usted tiene la tarea de asegurarse de que la interfaz pública se prueba a fondo. Esto significa que tienes que asegurarte de que cada ruta de código de la estructura AccountViewViewModel está cubierta por pruebas unitarias. En otras palabras, el conjunto de pruebas unitarias debe resultar en una cobertura completa del código. Eso incluye las entidades públicas, internas y privadas.

Si activamos la cobertura de código en Xcode y ejecutamos las pruebas unitarias de la estructura AccountViewViewModel, podemos ver que algunas rutas de código no se ejecutan.

Cobertura de código en Xcode

Esto nos indica que las pruebas unitarias están incompletas. Podemos ignorar la ruta de código para el error fatal. Yo nunca pruebo de forma unitaria las rutas de código que dan lugar a un error fatal, pero eso depende en gran medida de cómo uses los errores fatales en tus proyectos.

Podemos aumentar la cobertura de código para la estructura AccountViewViewModel añadiendo una prueba unitaria más.

func testExpiresAtAsString_20161225WithForwardSlashes() { let account = Account(expiresAt: "2016/12/25", subscription: .trial) let accountViewViewModel = AccountViewViewModel(account: account) XCTAssertEqual(accountViewViewModel.expiresAtAsString, "2016, December")}

Cobertura de código en Xcode

Implementación y especificación

Es importante entender que estamos probando la especificación de la estructura AccountViewViewModel. No estamos probando su implementación. Aunque esto pueda parecer similar, en realidad es muy diferente. Estamos probando la funcionalidad de la estructura AccountViewViewModel. No estamos interesados en cómo hace su magia bajo el capó.

La clave de este artículo es que las entidades privadas no necesitan ser probadas por unidades. Las pruebas unitarias son una forma de pruebas de caja negra. Esto significa que no probamos la implementación de la estructura AccountViewViewModel, probamos su especificación.

Sin embargo, esto no significa que no estemos interesados en la implementación. Tenemos que asegurarnos de que el conjunto de pruebas unitarias cubre cada ruta de código de la entidad que estamos probando. Los informes de cobertura de código son muy valiosos para lograr esto.

Descargue su copia gratuita de
El manual que falta
para el desarrollo de Swift

La guía que desearía haber tenido cuando empecé

Únase a más de 20.000 desarrolladores que están aprendiendo sobre el desarrollo de Swift

Descargue su copia gratuita

.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.