Framework de Testing
Framework de Testing
Sección titulada «Framework de Testing»El modulo wippy/test proporciona un framework de testing estilo BDD con aserciones, hooks de ciclo de vida y mocking.
Configuracion
Sección titulada «Configuracion»Agrega la dependencia:
wippy add wippy/testwippy installEl modulo registra un comando test automaticamente. Una vez instalado, wippy run test descubre y ejecuta todas las entradas de test en tu proyecto.
Definir Tests
Sección titulada «Definir Tests»Los tests son entradas function.lua con meta.type: test:
version: "1.0"namespace: app.test
entries: - name: math kind: function.lua meta: type: test suite: math name: Operaciones matematicas source: file://math_test.lua method: run imports: test: wippy.test:testMetadatos del Test
Sección titulada «Metadatos del Test»| Field | Required | Description |
|---|---|---|
type | Yes | Debe ser "test" para que el runner lo descubra |
suite | No | Agrupa tests en la salida del runner |
name | No | Nombre mostrado en la salida del runner |
order | No | Orden dentro de una suite (menor se ejecuta primero) |
Escribir Tests
Sección titulada «Escribir Tests»Estilo BDD
Sección titulada «Estilo BDD»Usa bloques describe e it para estructurar tests:
local test = require("test")
local function define_tests() test.describe("calculator", function() test.it("adds numbers", function() test.eq(1 + 1, 2) end)
test.it("multiplies numbers", function() test.eq(3 * 4, 12) end) end)end
local run_cases = test.run_cases(define_tests)
local function run(options) local result = run_cases(options) if result.failed_tests > 0 then error("tests failed: " .. result.failed_tests) end return resultend
return { run = run }Suites Anidadas
Sección titulada «Suites Anidadas»Las suites pueden anidarse para organizacion:
test.describe("user", function() test.describe("validation", function() test.it("requires name", function() test.ok(validate({}).error) end)
test.it("accepts valid input", function() test.is_nil(validate({name = "Alice"}).error) end) end)
test.describe("formatting", function() test.it("formats display name", function() test.eq(format_name("alice"), "Alice") end) end)end)Omitir Tests
Sección titulada «Omitir Tests»test.it_skip("not implemented yet", function() test.fail("TODO")end)Los tests omitidos aparecen en la salida pero no cuentan como fallos.
Alias de Suites
Sección titulada «Alias de Suites»test.spec y test.context son alias de test.describe:
test.spec("feature", function() test.context("when valid input", function() test.it("succeeds", function() test.ok(true) end) end)end)Aserciones
Sección titulada «Aserciones»Igualdad
Sección titulada «Igualdad»test.eq(actual, expected, msg?) -- actual == expectedtest.neq(actual, expected, msg?) -- actual ~= expectedVeracidad
Sección titulada «Veracidad»test.ok(val, msg?) -- val is truthytest.fail(msg?) -- unconditional failureVerificaciones de Nil
Sección titulada «Verificaciones de Nil»test.is_nil(val, msg?) -- val == niltest.not_nil(val, msg?) -- val ~= nilVerificaciones de Tipo
Sección titulada «Verificaciones de Tipo»test.is_true(val, msg?) -- val == truetest.is_false(val, msg?) -- val == falsetest.is_string(val, msg?)test.is_number(val, msg?)test.is_table(val, msg?)test.is_function(val, msg?)test.is_boolean(val, msg?)Strings y Colecciones
Sección titulada «Strings y Colecciones»test.contains(str, substr, msg?) -- substring matchtest.matches(str, pattern, msg?) -- Lua pattern matchtest.has_key(tbl, key, msg?) -- table key existstest.len(val, expected, msg?) -- #val == expectedComparaciones Numericas
Sección titulada «Comparaciones Numericas»test.gt(a, b, msg?) -- a > btest.gte(a, b, msg?) -- a >= btest.lt(a, b, msg?) -- a < btest.lte(a, b, msg?) -- a <= bManejo de Errores
Sección titulada «Manejo de Errores»test.throws(fn, msg?) -- fn() raises error, returns ittest.has_error(val, err, msg?) -- val is nil, err is not niltest.no_error(val, err, msg?) -- err is nilTodas las aserciones aceptan un mensaje opcional como ultimo argumento. En caso de fallo, el mensaje se incluye en la salida de error.
Hooks de Ciclo de Vida
Sección titulada «Hooks de Ciclo de Vida»test.describe("database", function() test.before_all(function() -- runs once before the suite db = connect() end)
test.after_all(function() -- runs once after the suite db:close() end)
test.before_each(function() -- runs before each test db:begin_transaction() end)
test.after_each(function() -- runs after each test db:rollback() end)
test.it("inserts a record", function() db:exec("INSERT INTO users (name) VALUES ('Alice')") local count = db:query_row("SELECT COUNT(*) FROM users") test.eq(count, 1) end)end)Los hooks en suites anidadas se ejecutan en orden: el before_each del padre se ejecuta antes del before_each del hijo, y el after_each del hijo se ejecuta antes del after_each del padre.
Mocking
Sección titulada «Mocking»El sistema de mock reemplaza campos de objetos globales y los restaura automaticamente despues de cada test.
Mocking Basico
Sección titulada «Mocking Basico»test.describe("notifications", function() test.it("sends message", function() local sent = false test.mock("process.send", function(pid, topic, payload) sent = true end)
notify_user("hello") test.is_true(sent) -- mock is auto-restored after this test end)end)API de Mock
Sección titulada «API de Mock»test.mock("object.field", replacement) -- replace a global fieldtest.mock_process("field", replacement) -- shorthand for process fieldstest.restore_mock("object.field") -- restore one mocktest.restore_all_mocks() -- restore all mocksLas rutas de mock usan notacion de punto: "process.send" reemplaza _G.process.send.
Los mocks para process.send redirigen automaticamente los mensajes del framework de testing a traves de la funcion original, para que el reporte de eventos de test continue funcionando cuando process.send esta mockeado.
Todos los mocks se restauran automaticamente despues de cada test mediante el hook after_each.
Ejecutar Tests
Sección titulada «Ejecutar Tests»Ejecutar Todos los Tests
Sección titulada «Ejecutar Todos los Tests»wippy run testFiltrar por Patron
Sección titulada «Filtrar por Patron»wippy run test mathwippy run test user validationLos filtros coinciden contra los IDs de las entradas. Multiples patrones se combinan.
Ejemplo de Salida
Sección titulada «Ejemplo de Salida»3 tests in 1 suites
calculator + adds numbers 0ms + multiplies numbers 0ms - divides by zero 1ms Error: expected error, got nil
1 suite | 2 passed | 1 failed | 0 skipped | 3msTests Simples
Sección titulada «Tests Simples»Para tests que no necesitan el framework BDD, define una funcion simple que retorne true o lance un error:
local funcs = require("funcs")
local function main() local result, err = funcs.call("app:my_function", "input") if err then error("call failed: " .. tostring(err)) end if result ~= "expected" then error("expected 'expected', got: " .. tostring(result)) end return trueend
return { main = main } - name: integration kind: function.lua meta: type: test suite: integration source: file://integration_test.lua method: main modules: - funcsEl runner detecta si un test usa eventos de casos BDD o retorna un valor simple. Ambos patrones funcionan con wippy run test.
Estructura del Proyecto
Sección titulada «Estructura del Proyecto»Un layout tipico de tests:
src/ _index.yaml app.lua test/ _index.yaml # test entries math_test.lua user_test.lua integration_test.luaEl _index.yaml de tests define el namespace y las entradas de test:
version: "1.0"namespace: app.test
entries: - name: math kind: function.lua meta: type: test suite: math source: file://math_test.lua method: run imports: test: wippy.test:test
- name: user kind: function.lua meta: type: test suite: user source: file://user_test.lua method: run imports: test: wippy.test:testRequisitos de Infraestructura
Sección titulada «Requisitos de Infraestructura»El runner de tests necesita un process.host y un terminal.host en tu aplicacion. Estos tipicamente ya estan presentes. Si no, agregalos:
entries: - name: processes kind: process.host lifecycle: auto_start: true
- name: terminal kind: terminal.host lifecycle: auto_start: trueVer Tambien
Sección titulada «Ver Tambien»- Descripcion General del Framework - Uso de modulos del framework
- Referencia CLI - Comandos CLI
- Funciones - Registro de funciones