Translate

martes, 28 de octubre de 2025

Unit Testing en Elm: confianza tipada desde el compilador hasta las pruebas


Elm ya es famoso por su compilador que evita errores en tiempo de ejecución, pero eso no significa que las pruebas no sean necesarias.

De hecho, los tests en Elm complementan su sistema de tipos, ayudándote a verificar la lógica de negocio, las transformaciones de datos y el comportamiento de tus funciones puras.

A diferencia de otros lenguajes donde los tests buscan prevenir errores de nulls, tipos o efectos secundarios, en Elm los tests sirven principalmente para:

  • Asegurar que una función devuelva el resultado esperado.
  • Comprobar que una actualización de modelo (en TEA) cambie el estado correctamente.
  • Validar transformaciones de datos o funciones puras de negocio.


Gracias a la pureza funcional, las pruebas en Elm son simples y predecibles.

El framework oficial de testing es elm-test.


Instalalo con:


npm install -g elm-test

elm-test init


Esto crea una carpeta:


tests/

 └── Example.elm


donde podrás escribir tus pruebas.


Supongamos que tenés una función en src/MathUtils.elm:


module MathUtils exposing (add)


add : Int -> Int -> Int

add a b =

    a + b


Podés crear un test en tests/MathUtilsTest.elm:


module MathUtilsTest exposing (tests)


import Expect

import Test exposing (..)

import MathUtils exposing (add)


tests : Test

tests =

    describe "Pruebas de MathUtils"

        [ test "Suma básica" <|

            \_ -> Expect.equal 4 (add 2 2)

        , test "Suma con negativos" <|

            \_ -> Expect.equal 0 (add 2 -2)

        ]


Ejecutalo con:

elm-test


y deberías ver algo como:


TEST RUN PASSED

Duration: 15 ms


Podés probar también las funciones update de tu arquitectura Elm.

Supongamos una aplicación simple que incrementa un contador:


type alias Model =

    { counter : Int }


type Msg

    = Increment


update : Msg -> Model -> Model

update msg model =

    case msg of

        Increment ->

            { model | counter = model.counter + 1 }


El test sería:


module CounterTest exposing (tests)


import Expect

import Test exposing (..)

import Main exposing (update, Model(..), Msg(..))


tests : Test

tests =

    describe "Update del contador"

        [ test "Incrementa el contador" <|

            \_ ->

                let

                    model = { counter = 0 }

                    updated = update Increment model

                in

                Expect.equal 1 updated.counter

        ]



Expect.notEqual – asegura que dos valores sean diferentes.

Expect.true / Expect.false – comprueba booleanos.

Expect.all – combina varios asserts sobre el mismo valor.

Fuzz tests – pruebas con datos aleatorios (muy útiles para funciones matemáticas o validaciones).


Ejemplo de fuzzing:


import Fuzz exposing (int)


fuzzTest : Test

fuzzTest =

    fuzz int "Suma con cero no cambia el número" <|

        \n -> Expect.equal n (add n 0)


El fuzzer genera cientos de casos aleatorios para asegurar que la propiedad siempre se cumple.

El sistema de tipos de Elm ya evita gran parte de los errores comunes, pero las pruebas unitarias llevan esa confianza un paso más allá. Te permiten documentar y verificar el comportamiento de tus funciones de forma declarativa, legible y segura.