Muy buenas, soy Miguel y hoy les traigo este nuevo artículo.
Las funciones puras, tal como las define la Programación funcional, pueden proporcionar un conjunto simple de reglas a seguir para mejorar la capacidad de prueba del código.
Índice
🤔 ¿Qué son las funciones puras?
Las funciones puras, cuando se llaman con una entrada determinada, calculan y devuelven resultados y no hacen nada más. Es un mapeo de valores a valores.
Las funciones puras deben verificar estas tres reglas:
- total: hay un resultado para cualquier entrada posible
- determinista: el resultado siempre será el mismo si la entrada es la misma
- sin efectos secundarios: la función no hace más que calcular el resultado
Las funciones puras son cruciales en la programación funcional para la “transparencia referencial”. Permite la composición de funciones de una manera sobre la que podemos razonar, al igual que las matemáticas. Pero incluso si no está interesado en adoptar la programación funcional, puede usar esta definición para mejorar su código.
😰Por qué las funciones impuras pueden hacer que su código sea más difícil de probar
Las funciones impuras (funciones que rompen una o más de las reglas enumeradas anteriormente) son más difíciles de trabajar:
- UN función parcial es uno que tiene condiciones previas. El compilador no puede ayudarlo a asegurarse de que siempre esté verificando las condiciones previas. Necesita leer el código o la documentación para conocerlos y escribir código y pruebas para eso. Llamar a la función con una entrada no admitida causará problemas (por ejemplo, dividir por cero)
- UN función no determinista puede devolver resultados diferentes cuando se llama con una entrada determinada. Difícilmente se pueden probar estas funciones, ya que no se puede llamar a la función con una entrada fija y comparar el resultado con un valor esperado.
- Funciones con efectos secundarios, al igual que las funciones parciales, requieren conocimiento de sus aspectos internos. Hace más de lo que dice en el cuadro si el cuadro es la firma de la función. Entonces, al igual que las funciones parciales, los compiladores no pueden ayudarlo. Esta vez, sin embargo, el problema no es que alguna entrada no sea válida, es que de alguna manera, parte del resultado está «oculto».
🐛La impureza se propaga
Una cosa importante a tener en cuenta es que la impureza tiende a extenderse. Tomemos un ejemplo de una función que me puede decir cuántas cookies puedo tener hoy. Cuando es mi cumpleaños, puedo tener dos galletas en lugar de una:
func isMyBirthday() -> Bool { let today = Date() let components = Calendar(identifier: .gregorian) .dateComponents([.day, .month], from: today) return components.day == 7 && components.month == 4 } func maximumCookies() -> Int { isMyBirthday() ? 2 : 1 }
¡Deja esa galleta! ¡Ahora!
En «es mi cumpleaños()», usamos «Fecha()» que es una función no determinista (crea un objeto Date con la fecha y hora actual que no devolverá el mismo resultado si llamamos a la función hoy y mañana). «es mi cumpleaños()» se vuelve no determinista, como resultado, cambiará con el tiempo.
Finalmente, porque «es mi cumpleaños()» es no determinista, «MaximumCookie ()» también lo será (la función devuelve un valor diferente si hoy es mi cumpleaños) y cualquier función que lo use, y así sucesivamente.
Lo mismo sucedería con funciones parciales o funciones con efectos secundarios.
Podemos ver que el uso de una función impura no es un problema local para la capacidad de prueba y puede tener un impacto significativo en nuestra cobertura de prueba.
🧹Refactorización de funciones impuras
Entonces, ¿podemos hacer puras todas nuestras funciones? No. Todavía necesitará algunas funciones impuras para escribir cualquier aplicación valiosa, como llamar a un punto final, almacenar un valor en UserDefaults, obtener la configuración regional actual para formatear números, etc. Pero podemos intentar eliminar funciones impuras de las partes de nuestro aplicación que más importa, como nuestra lógica empresarial, que nos gustaría cubrir con pruebas.
Siempre que encuentre una función que sea difícil de probar, identifique qué regla no se cumple y siga a los infractores hasta llegar a un fragmento de código que causa los problemas y no puede cambiarlo. Para nuestro ejemplo, sería cuando llamamos «Fecha()» en «es mi cumpleaños()». Comience fijando la persona que llama de esta función:
- ¿La función es parcial? Puede cambiar el tipo de entrada para hacer cumplir las condiciones previas, o puede cambiar el tipo de devolución para tener en cuenta las entradas no admitidas («Opcional» funciona bien, o «Resultado» si desea proporcionar detalles sobre la condición previa rota)
- ¿La función es no determinista? Es posible que desee encontrar una manera de inyectar el estado del que depende (podría ser un valor o una función)
- ¿Tiene la función algún efecto secundario? Cambie su tipo de retorno a uno que describa el efecto secundario, sin realizarlo, o pase la función impura como parámetro. Ejecute los efectos, o defina y pase la función impura, en la capa exterior de su aplicación, por ejemplo, en su UIViewController.
Su función ahora es pura y necesitará refactorizar el código usándola. Repita hasta que pueda probar su función problemática inicial.
Refactoricemos nuestro ejemplo:
func isMyBirthday(_ today: Date) -> Bool { let components = Calendar(identifier: .gregorian) .dateComponents([.day, .month], from: today) return components.day == 7 && components.month == 4 } func maximumCookies(_ today: Date) -> Int { isMyBirthday(today) ? 2 : 1 }
Así es como usa la función, por ejemplo, en su UIViewController:
maximumCookies(Date())
Ahora puede probar su lógica de distribución de cookies:
XCTAssertEqual(2, maximumCookies(Date(timeIntervalSince1970: 1586260800))) // 7 April 2020 12:00:00 XCTAssertEqual(1, maximumCookies(Date(timeIntervalSince1970: 1586347200))) // 8 April 2020 12:00:00
🍪Palabras de despedida
Comprender qué hace que una función sea impura es una manera fácil de saber por qué un fragmento de código es difícil de probar. La refactorización aún requiere un poco de práctica, pero lo hace más fácil. Las funciones puras también pueden ayudar mucho más que esto.
Añadir comentario