Pruebas y calidad de código

Código heredado: prueba el comportamiento antes de refactorizar

Nueve casos límite de una función de descuentos permiten distinguir una refactorización de un cambio de reglas y detectar resultados negativos.

2 min de lectura
Imagen de portada de Código heredado: prueba el comportamiento antes de refactorizar

Para modificar código heredado con seguridad, registra sus resultados actuales y separa los cambios estructurales de los cambios de reglas. Una prueba de caracterización describe el comportamiento existente; no certifica que sea correcto.

Mecanismo y decisión de diseño

Refactorizar modifica la estructura interna conservando el comportamiento observable. Si desconoces la regla, registra límites de ramas, valores ausentes y rutas de error, además de entradas normales. Aquí la función original resta 30 a un importe positivo para clientes de tipo B. Fuente primaria

Código reproducible

Este ejemplo construido sustituye el condicional original por una expresión de descuento. Nueve casos cubren -1, 0, 1, 29, 30, 31, 100 y otro tipo de cliente. Los valores esperados se escriben en una tabla independiente, no mediante llamadas a la función antigua.

Este ejemplo independiente se ejecutó el 2026-10-23 con Apple Swift 6.4 para arm64 macOS, en modo de lenguaje Swift 6. No necesita red ni paquetes adicionales.

Descargar el archivo Swift completo

Ejecuta el comando en la carpeta de descarga. Las precondiciones detienen la ejecución si falla una comprobación. No uses -Ounchecked: elimina estas comprobaciones.

swift -swift-version 6 legacy-code-maintenance-problems.swift
func legacyPrice(kind: String, amount: Int) -> Int {
    if kind == "B" && amount > 0 { return amount - 30 }
    return amount
}

func refactoredPrice(kind: String, amount: Int) -> Int {
    let discount = kind == "B" && amount > 0 ? 30 : 0
    return amount - discount
}

let cases: [(String, Int, Int)] = [
    ("B", -1, -1), ("B", 0, 0), ("B", 1, -29),
    ("B", 29, -1), ("B", 30, 0), ("B", 31, 1),
    ("B", 100, 70), ("A", 100, 100), ("A", 0, 0)
]
var clampDifferences = 0
for (kind, amount, expected) in cases {
    precondition(legacyPrice(kind: kind, amount: amount) == expected)
    precondition(refactoredPrice(kind: kind, amount: amount) == expected)
    let changed = kind == "B" && amount > 0 ? max(0, amount - 30) : amount
    if changed != expected { clampDifferences += 1 }
}
precondition(clampDifferences == 2)
print("cases=9 preserved=9 clamp-differences=2")

Salida registrada e interpretación

cases=9 preserved=9 clamp-differences=2

Ambas implementaciones coinciden en los nueve resultados. Limitar los totales negativos a cero cambia los casos de importe 1 y 29. La comprobación separada clamp-differences=2 demuestra ese cambio de reglas; no oculta un fallo de preservación.

Alcance y limitaciones

Esto no recomienda aceptar cobros negativos. Confirma la política real con su responsable. El experimento usa enteros pequeños y no verifica impuestos, redondeo monetario, desbordamientos ni efectos de una base de datos. Nueve casos tampoco prueban equivalencia para todas las entradas.

Qué comprobar después

Examina las llamadas y los datos guardados para ampliar los casos. Si deben prohibirse los totales negativos, acuerda nuevas expectativas y aplica el cambio por separado. Si la base de datos impide aislar pruebas, continúa con el ejemplo de inyección del almacenamiento.

Experimento relacionado

Fuentes y verificación

  • Definition of RefactoringMartin Fowler · Fuente original del autor · Consultado 23 de octubre de 2026Respalda: Fuente del contrato de implementación y de la explicación. La salida se comprobó por separado con el experimento incluido aquí.