{"id":602,"date":"2026-07-22T08:16:54","date_gmt":"2026-07-22T08:16:54","guid":{"rendered":"https:\/\/matforge.org\/?p=602","raw":"https:\/\/matforge.org\/?p=602"},"modified":"2026-07-22T08:16:54","modified_gmt":"2026-07-22T08:16:54","slug":"unit-testing-scientific-code-pytest-strategies-research-projects","status":"publish","type":"post","link":"https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/","title":{"rendered":"Pruebas unitarias para c\u00f3digo cient\u00edfico: estrategias PYTEST para proyectos de investigaci\u00f3n","raw":"Pruebas unitarias para c\u00f3digo cient\u00edfico: estrategias PYTEST para proyectos de investigaci\u00f3n"},"content":{"rendered":"<span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Reading Time: <\/span> <span class=\"rt-time\"> 11<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span><p>Las pruebas unitarias no son negociables para software cient\u00edfico de confianza. A diferencia de las aplicaciones comerciales, el c\u00f3digo de investigaci\u00f3n a menudo carece de pruebas formales, lo que conduce a resultados irreproducibles y un esfuerzo desperdiciado. Esta gu\u00eda cubre las estrategias de PyTest espec\u00edficamente para proyectos cient\u00edficos de Python: manejo de precisi\u00f3n num\u00e9rica con <code>pytest.approx<\/code>, aislando dependencias externas con simulacro, uso de accesorios y parametrizaci\u00f3n de manera eficiente e integrando pruebas en canalizaciones de integraci\u00f3n continua. Aprender\u00e1 cu\u00e1ndo usar la validaci\u00f3n de caja negra con los resultados publicados y c\u00f3mo dise\u00f1ar pruebas que sobrevivan a la evoluci\u00f3n del c\u00f3digo sin volverse fr\u00e1gil.<\/p>\n<h2>Por qu\u00e9 es importante la prueba unitaria en el software de investigaci\u00f3n<\/h2>\n<p>El software cient\u00edfico existe en un espacio desafiante. Debe ser lo suficientemente flexible como para explorar nuevas hip\u00f3tesis, pero lo suficientemente confiable como para que los resultados publicados puedan reproducirse meses o a\u00f1os despu\u00e9s. A diferencia del software comercial con especificaciones claras, el c\u00f3digo de investigaci\u00f3n a menudo evoluciona junto con los experimentos, y los requisitos cambian a medida que surgen nuevos descubrimientos.<\/p>\n<p>Las consecuencias de las pruebas inadecuadas en la investigaci\u00f3n son graves:<\/p>\n<ul>\n<li><strong>Resultados irreproducibles<\/strong>: diferentes investigadores obtienen diferentes resultados del mismo c\u00f3digo<\/li>\n<li><strong>Errores silenciosos<\/strong>: errores num\u00e9ricos que parecen peque\u00f1os individualmente compuestos en inexactitudes significativas<\/li>\n<li><strong>P\u00e9rdida de conocimiento<\/strong>: cuando los desarrolladores originales se van, las pruebas sirven como documentaci\u00f3n ejecutable<\/li>\n<li><strong>Esfuerzo desperdiciado<\/strong>: la depuraci\u00f3n se convierte en una caza de detectives en lugar de un proceso sistem\u00e1tico<\/li>\n<\/ul>\n<p>Las pruebas unitarias abordan estos problemas validando componentes peque\u00f1os y aislados de su c\u00f3digo. Cada prueba verifica que una funci\u00f3n o clase espec\u00edfica se comporta como se espera dadas las entradas definidas. Cuando las pruebas pasan de manera consistente entre entornos, tiene evidencia de que su c\u00f3digo produce resultados confiables.<\/p>\n<p>Pero probar el c\u00f3digo cient\u00edfico presenta desaf\u00edos \u00fanicos que los enfoques de prueba de software est\u00e1ndar no abordan completamente.<\/p>\n<h2>Desaf\u00edos \u00fanicos de probar el c\u00f3digo cient\u00edfico<\/h2>\n<p>El c\u00f3digo cient\u00edfico y num\u00e9rico difiere de las aplicaciones comerciales t\u00edpicas de varias maneras que afectan la estrategia de prueba.<\/p>\n<h3>Precisi\u00f3n num\u00e9rica y errores de punto flotante<\/h3>\n<p>La aritm\u00e9tica de punto flotante es inherentemente imprecisa. Debido a c\u00f3mo los equipos representan n\u00fameros decimales, <code>0.1 + 0.2<\/code> no es exactamente <code>0.3<\/code> en la representaci\u00f3n de punto flotante binario. En simulaciones cient\u00edficas que involucran miles o millones de operaciones, estos peque\u00f1os errores se acumulan.<\/p>\n<p>Una prueba ingenua que utiliza una igualdad exacta (<code>==<\/code>) fallar\u00e1 de forma intermitente o en diferentes hardware:<\/p>\n<pre><code class=\"language-python\">def test_numerical_computation():\n    result = complex_simulation()  # returns 1.0000000000000002\n    assert result == 1.0  # FAILS! Even though the difference is negligible\n<\/code><\/pre>\n<p>La soluci\u00f3n es utilizar comparaciones basadas en la tolerancia. PyTest proporciona <code>pytest.approx()<\/code> para este prop\u00f3sito:<\/p>\n<pre><code class=\"language-python\">def test_numerical_computation():\n    result = complex_simulation()\n    assert result == pytest.approx(1.0, rel=1e-9, abs=1e-12)\n<\/code><\/pre>\n<p><code>rel<\/code> (tolerancia relativa) es \u00fatil para valores de cualquier magnitud; <code>abs<\/code> (tolerancia absoluta) maneja casos en los que el valor esperado es cercano a cero. Los valores predeterminados son <code>rel=1e-6<\/code> y <code>abs=1e-12<\/code>, pero el c\u00f3digo cient\u00edfico a menudo requiere tolerancias m\u00e1s estrictas.<\/p>\n<h3>Respuestas \u00abcorrectas\u00bb desconocidas<\/h3>\n<p>En muchos escenarios de investigaci\u00f3n, no tiene una salida correcta conocida. La simulaci\u00f3n podr\u00eda estar explorando territorio desconocido. \u00bfC\u00f3mo se prueba el c\u00f3digo cuando no sabe cu\u00e1l deber\u00eda ser la respuesta?<\/p>\n<p>Varias estrategias funcionan:<\/p>\n<ol>\n<li><strong>Operaciones inversas<\/strong>: Si su c\u00f3digo calcula <code>B = f(A)<\/code>, tambi\u00e9n pruebe que <code>A \u2248 f\u207b\u00b9(B)<\/code>.<\/li>\n<li><strong>Leyes de conservaci\u00f3n<\/strong>: para simulaciones f\u00edsicas, verifique que la masa, la energ\u00eda o el impulso se conserven dentro de la tolerancia.<\/li>\n<li><strong>Casos limitantes<\/strong>: Comportamiento de prueba en l\u00edmites simplificados donde existen soluciones anal\u00edticas.<\/li>\n<li><strong>Pruebas de regresi\u00f3n<\/strong>: Almacene los resultados de una ejecuci\u00f3n de confianza y detecte cambios inesperados.<\/li>\n<\/ol>\n<h3>Dependencias externas y c\u00e1lculo pesado<\/h3>\n<p>El c\u00f3digo cient\u00edfico a menudo depende de:<\/p>\n<ul>\n<li>Grandes conjuntos de datos (terabytes de entrada de simulaci\u00f3n)<\/li>\n<li>Solvers o Bibliotecas externas (Paquetes HPC, Bibliotecas Fortran)<\/li>\n<li>E\/S de archivo con formatos complejos<\/li>\n<li>Conexiones de base de datos o API<\/li>\n<\/ul>\n<p>Ejecutar el sistema completo en cada prueba unitaria no es pr\u00e1ctico. Necesitas aislamiento.<\/p>\n<h2>Caracter\u00edsticas de PyTest que resuelven problemas de prueba de investigaci\u00f3n<\/h2>\n<p>PyTest proporciona varias caracter\u00edsticas que son particularmente valiosas para el c\u00f3digo cient\u00edfico.<\/p>\n<h3>Accesorios para la instalaci\u00f3n y el desmontaje<\/h3>\n<p>Los accesorios encapsulan el c\u00f3digo de instalaci\u00f3n que se ejecuta antes de las pruebas. Para las pruebas cient\u00edficas, los accesorios pueden:<\/p>\n<ul>\n<li>Crear datos o mallas de prueba temporales<\/li>\n<li>Inicializar objetos de simulaci\u00f3n con par\u00e1metros conocidos<\/li>\n<li>Limpiar archivos temporales despu\u00e9s de las pruebas<\/li>\n<li>Proporcionar configuraciones de prueba reutilizables<\/li>\n<\/ul>\n<pre><code class=\"language-python\">import pytest\nimport tempfile\nimport numpy as np\n\n@pytest.fixture\ndef simple_mesh():\n    \"\"\"Create a small 1D mesh for testing.\"\"\"\n    from fipy import Grid1D\n    return Grid1D(dx=0.1, nx=10)\n\n@pytest.fixture\ndef diffusion_solver(simple_mesh):\n    \"\"\"Set up a diffusion solver on the test mesh.\"\"\"\n    from fipy import CellVariable, DiffusionTerm\n    var = CellVariable(name=\"concentration\", mesh=simple_mesh, value=1.0)\n    eq = DiffusionTerm(coeff=1.0) == 0\n    return var, eq\n<\/code><\/pre>\n<h3>Parametrizaci\u00f3n para m\u00faltiples escenarios<\/h3>\n<p>En lugar de escribir funciones de prueba separadas para casos similares, use <code>@pytest.mark.parametrize<\/code> para ejecutar la misma l\u00f3gica de prueba con diferentes entradas.<\/p>\n<pre><code class=\"language-python\">@pytest.mark.parametrize(\"dx,nx,expected_volume\", [\n    (0.1, 10, 1.0),\n    (0.01, 100, 1.0),\n    (0.001, 1000, 1.0),\n])\ndef test_mesh_volume(dx, nx, expected_volume):\n    \"\"\"Test that mesh volume matches domain size.\"\"\"\n    mesh = Grid1D(dx=dx, nx=nx)\n    assert mesh.cellVolumes.sum() == pytest.approx(expected_volume)\n<\/code><\/pre>\n<p>La parametrizaci\u00f3n es especialmente \u00fatil para:<\/p>\n<ul>\n<li>Casos de prueba de borde (valores cero, n\u00fameros muy peque\u00f1os\/grandes)<\/li>\n<li>Verificaci\u00f3n del comportamiento en diferentes resoluciones de malla<\/li>\n<li>Validaci\u00f3n de varios tipos de condiciones de contorno<\/li>\n<li>Comprobaci\u00f3n de varios valores de propiedad del material<\/li>\n<\/ul>\n<p>Para el c\u00f3digo de investigaci\u00f3n, puede parametrizar contra resultados de referencia conocidos de art\u00edculos publicados.<\/p>\n<h3>Burlarse de las dependencias externas<\/h3>\n<p>La burla reemplaza a las dependencias reales con falsificaciones controladas. Esto a\u00edsla la unidad bajo prueba y hace que las pruebas sean m\u00e1s r\u00e1pidas y confiables.<\/p>\n<p><strong>Cu\u00e1ndo simular en c\u00f3digo cient\u00edfico:<\/strong><\/p>\n<ul>\n<li>Archivos de datos externos: reemplace los grandes conjuntos de datos con datos sint\u00e9ticos m\u00ednimos que ejercen las mismas rutas de c\u00f3digo<\/li>\n<li>Solvers HPC: simulacros costosas de Fortran con implementaciones de Python puras que devuelven resultados conocidos<\/li>\n<li>API de red: servicios remotos de stub que proporcionan par\u00e1metros o configuraci\u00f3n<\/li>\n<li>Generadores de n\u00fameros aleatorios: sembrarlos para producir secuencias deterministas<\/li>\n<\/ul>\n<pre><code class=\"language-python\">from unittest.mock import patch, MagicMock\n\ndef test_simulation_with_external_data():\n    # Mock the data loading function to return small, known data\n    with patch('mycode.load_large_dataset') as mock_load:\n        mock_load.return_value = np.array([1.0, 2.0, 3.0])\n        result = run_simulation()\n        assert result.converged\n<\/code><\/pre>\n<p><strong>Directriz importante<\/strong>: simula las dependencias de su propio c\u00f3digo, no las bibliotecas de terceros que no controla. Sigue el principio \u00abNo te burles de lo que no tienes\u00bb.<\/p>\n<h3>Usando <code>pytest.approx<\/code> para comparaciones num\u00e9ricas<\/h3>\n<p>Las comparaciones de punto flotante deben tener en cuenta los errores de redondeo. El objeto <code>approx<\/code> de PyTest maneja esto de forma elegante:<\/p>\n<pre><code class=\"language-python\">def test_diffusion_solution():\n    \"\"\"Test that diffusion reaches expected steady state.\"\"\"\n    concentration = solve_diffusion(time=100.0)\n    expected = 0.5  # analytical steady state for this boundary condition\n    assert concentration.mean() == pytest.approx(expected, rel=1e-6)\n<\/code><\/pre>\n<p>Tambi\u00e9n puede usar <code>approx<\/code> con matrices:<\/p>\n<pre><code class=\"language-python\">def test_array_computation():\n    result = compute_field()\n    expected = np.array([1.0, 2.0, 3.0])\n    assert result == pytest.approx(expected)\n<\/code><\/pre>\n<p>Para el c\u00f3digo cient\u00edfico, elija tolerancias basadas en:<\/p>\n<ul>\n<li>La precisi\u00f3n num\u00e9rica de sus m\u00e9todos (por ejemplo, la diferencia finita de segundo orden tiene error de truncamiento O(dx\u00b2))<\/li>\n<li>La precisi\u00f3n requerida por su aplicaci\u00f3n (tolerancia de ingenier\u00eda frente a investigaci\u00f3n exploratoria)<\/li>\n<\/ul>\n<h2>Desarrollo basado en pruebas para proyectos de investigaci\u00f3n<\/h2>\n<p>El desarrollo basado en pruebas (TDD) sigue un ciclo simple: escriba una prueba defectuosa, luego escriba un c\u00f3digo m\u00ednimo para que pase, luego refactorice. Si bien TDD est\u00e1 bien establecido en el software comercial, los proyectos de investigaci\u00f3n a menudo se resisten debido a las limitaciones de tiempo percibidas.<\/p>\n<p>La realidad: TDD ahorra tiempo en la investigaci\u00f3n al detectar errores antes de que se propaguen a trav\u00e9s de experimentos. Las pruebas de escritura primero lo obligan a aclarar la interfaz y el comportamiento esperado de cada funci\u00f3n antes de la implementaci\u00f3n.<\/p>\n<p><strong>TDD adaptado para la exploraci\u00f3n cient\u00edfica:<\/strong><\/p>\n<ol>\n<li>Comience con un modelo o algoritmo simple que entienda anal\u00edticamente<\/li>\n<li>Escribir pruebas que validen contra resultados conocidos (soluciones anal\u00edticas, casos limitantes)<\/li>\n<li>Implementar el c\u00f3digo para pasar esas pruebas<\/li>\n<li>Extender el modelo de forma incremental, agregando pruebas para cada nueva capacidad<\/li>\n<li>Cuando descubra un error, escriba una prueba que lo reproduzca primero, luego corrija<\/li>\n<\/ol>\n<p>TDD funciona bien para:<\/p>\n<ul>\n<li>Funciones de utilidad (generaci\u00f3n de malla, transformaciones de coordenadas)<\/li>\n<li>Operaciones Matem\u00e1ticas (Manipulaciones de Matriz, Funciones Especiales)<\/li>\n<li>Pipelines de procesamiento de datos (an\u00e1lisis, filtrado, normalizaci\u00f3n)<\/li>\n<li>Validaci\u00f3n de configuraci\u00f3n<\/li>\n<\/ul>\n<p>TDD es menos adecuado para:<\/p>\n<ul>\n<li>C\u00f3digo altamente exploratorio donde la interfaz en s\u00ed es incierta<\/li>\n<li>Scripts \u00fanicos que no se reutilizar\u00e1n<\/li>\n<li>C\u00f3digo que depende de los recursos externos a\u00fan no disponibles<\/li>\n<\/ul>\n<p>En la pr\u00e1ctica, un enfoque h\u00edbrido funciona mejor: escribir pruebas para componentes b\u00e1sicos y estables; Utilice pruebas de integraci\u00f3n m\u00e1s ligeras para secciones experimentales.<\/p>\n<h2>Organizaci\u00f3n de pruebas para proyectos cient\u00edficos<\/h2>\n<p>\u00bfD\u00f3nde deber\u00edan vivir los archivos de prueba? PyTest ofrece flexibilidad:<\/p>\n<pre><code>my_research_project\/\n\u251c\u2500\u2500 src\/\n\u2502   \u2514\u2500\u2500 mypackage\/\n\u2502       \u251c\u2500\u2500 __init__.py\n\u2502       \u251c\u2500\u2500 solver.py\n\u2502       \u2514\u2500\u2500 mesh.py\n\u251c\u2500\u2500 tests\/\n\u2502   \u251c\u2500\u2500 __init__.py\n\u2502   \u251c\u2500\u2500 test_solver.py\n\u2502   \u251c\u2500\u2500 test_mesh.py\n\u2502   \u2514\u2500\u2500 conftest.py  # shared fixtures\n\u251c\u2500\u2500 data\/\n\u2502   \u2514\u2500\u2500 reference_results\/  # stored outputs for regression tests\n\u251c\u2500\u2500 .github\/\n\u2502   \u2514\u2500\u2500 workflows\/\n\u2502       \u2514\u2500\u2500 ci.yml  # GitHub Actions CI configuration\n\u251c\u2500\u2500 pyproject.toml\n\u2514\u2500\u2500 README.md\n<\/code><\/pre>\n<p><strong>Convenciones clave:<\/strong><\/p>\n<ul>\n<li>Mantenga las pruebas en un directorio separado <code>tests\/<\/code> paralelo a <code>src\/<\/code> (o <code>lib\/<\/code>)<\/li>\n<li>Nombre de archivo de prueba <code>test_*.py<\/code> o <code>*_test.py<\/code><\/li>\n<li>Funciones de prueba de nombre <code>test_*()<\/code> Para permitir el descubrimiento autom\u00e1tico de PyTest<\/li>\n<li>Use <code>conftest.py<\/code> para accesorios compartidos en varios archivos de prueba<\/li>\n<\/ul>\n<p>Para proyectos basados en FIPY, pruebas de estructura para que coincidan con la jerarqu\u00eda de m\u00f3dulos:<\/p>\n<pre><code>fipy_project\/\n\u251c\u2500\u2500 fipy\/\n\u2502   \u251c\u2500\u2500 meshes\/\n\u2502   \u2502   \u2514\u2500\u2500 grid1d.py\n\u2502   \u2514\u2500\u2500 terms\/\n\u2502       \u2514\u2500\u2500 diffusion.py\n\u251c\u2500\u2500 tests\/\n\u2502   \u251c\u2500\u2500 meshes\/\n\u2502   \u2502   \u2514\u2500\u2500 test_grid1d.py\n\u2502   \u2514\u2500\u2500 terms\/\n\u2502       \u2514\u2500\u2500 test_diffusion.py\n<\/code><\/pre>\n<h2>Integraci\u00f3n con integraci\u00f3n continua<\/h2>\n<p>Las pruebas unitarias solo proporcionan valor si se ejecutan de manera consistente. La integraci\u00f3n continua (CI) automatiza la ejecuci\u00f3n de la prueba cada vez que cambia el c\u00f3digo.<\/p>\n<p>Acciones de GitHub proporciona una configuraci\u00f3n de CI sencilla para proyectos de Python:<\/p>\n<pre><code class=\"language-yaml\"># .github\/workflows\/ci.yml\nname: CI\n\non: [push, pull_request]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [\"3.9\", \"3.10\", \"3.11\"]\n\n    steps:\n    - uses: actions\/checkout@v3\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions\/setup-python@v4\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install -e .[test]\n    - name: Run tests with pytest\n      run: |\n        pytest --cov=src --cov-report=xml --cov-report=html\n    - name: Upload coverage\n      uses: codecov\/codecov-action@v3\n<\/code><\/pre>\n<p>CI asegura:<\/p>\n<ul>\n<li>Las pruebas pasan en m\u00faltiples plataformas (Linux, macOS, Windows)<\/li>\n<li>Las pruebas pasan en varias versiones de Python<\/li>\n<li>La cobertura del c\u00f3digo se rastrea a lo largo del tiempo<\/li>\n<li>Las solicitudes de extracci\u00f3n se validan antes de fusionar<\/li>\n<\/ul>\n<p>Para el software de investigaci\u00f3n, considere agregar:<\/p>\n<ul>\n<li>Pruebas que se ejecutan con diferentes versiones de dependencias clave (numpy, scipy, fipy)<\/li>\n<li>Comprobaciones de regresi\u00f3n de rendimiento (asegurar que los algoritmos no se ralenticen)<\/li>\n<li>Las compilaciones de documentaci\u00f3n para verificar los ejemplos a\u00fan funcionan<\/li>\n<\/ul>\n<h2>Errores comunes y c\u00f3mo evitarlos<\/h2>\n<p>Con base en las mejores pr\u00e1cticas de investigaci\u00f3n y de la industria, aqu\u00ed est\u00e1n los errores de prueba unitarias m\u00e1s comunes en el c\u00f3digo cient\u00edfico:<\/p>\n<h3>1. Prueba de detalles de implementaci\u00f3n en lugar de comportamiento<\/h3>\n<p>Probar la implementaci\u00f3n interna hace que las pruebas sean fr\u00e1giles. Cuando refactoriza el c\u00f3digo, las pruebas a\u00fan deben pasar si el comportamiento externo es correcto.<\/p>\n<pre><code class=\"language-python\"># \u274c Bad: tests internal state\ndef test_algorithm_updates_counter():\n    obj = MyAlgorithm()\n    obj.step()\n    assert obj.counter == 1  # fragile if counter implementation changes\n\n# \u2705 Better: tests observable outcome\ndef test_algorithm_produces_correct_result():\n    obj = MyAlgorithm()\n    result = obj.run()\n    assert result == expected\n<\/code><\/pre>\n<h3>2. Ignorar la tolerancia num\u00e9rica<\/h3>\n<p>Las comparaciones exactas en los resultados de coma flotante provocan pruebas descamadas que fallan aleatoriamente o en diferentes hardware. Utilice siempre <code>pytest.approx()<\/code> o afirmaciones similares basadas en tolerancia para salidas num\u00e9ricas.<\/p>\n<h3>3. Escribir pruebas lentas<\/h3>\n<p>Las pruebas unitarias deben ejecutarse r\u00e1pidamente (milisegundos, no segundos). Si una prueba es lenta:<\/p>\n<ul>\n<li>no se ejecutar\u00e1 con la frecuencia suficiente<\/li>\n<li>Los desarrolladores omitir\u00e1n la ejecuci\u00f3n de la suite de pruebas completa<\/li>\n<li>IC se vuelve caro y lento<\/li>\n<\/ul>\n<p><strong>Soluciones:<\/strong><\/p>\n<ul>\n<li>Utilice peque\u00f1os conjuntos de datos sint\u00e9ticos en lugar de grandes reales<\/li>\n<li>simulacros de c\u00e1lculos externos caros<\/li>\n<li>Separe las pruebas de integraci\u00f3n lenta de las pruebas unitarias r\u00e1pidas<\/li>\n<li>Use la parametrizaci\u00f3n sabiamente: no ejecute miles de variaciones en cada pase de prueba<\/li>\n<\/ul>\n<h3>4. No aislar las pruebas<\/h3>\n<p>Las pruebas no deben depender unas de otras ni del estado global. Cada prueba debe:<\/p>\n<ul>\n<li>Cree sus propios datos de prueba (utilice accesorios)<\/li>\n<li>Limpiar despu\u00e9s de s\u00ed mismo<\/li>\n<li>no depender de la orden de ejecuci\u00f3n<\/li>\n<\/ul>\n<pre><code class=\"language-python\"># \u274c Bad: shared mutable state\nresults = []\n\ndef test_first():\n    results.append(1)\n\ndef test_second():\n    assert results == [1]  # fails if tests run in wrong order\n\n# \u2705 Better: independent tests\ndef test_first():\n    result = compute_something()\n    assert result == 1\n\ndef test_second():\n    result = compute_something_else()\n    assert result == 2\n<\/code><\/pre>\n<h3>5. Saltarse las pruebas sin una buena raz\u00f3n<\/h3>\n<p><code>@pytest.mark.skip<\/code> se debe usar con moderaci\u00f3n. Si se omite una prueba porque el entorno carece de algo, use <code>pytest.importorskip()<\/code> a nivel de m\u00f3dulo o haga que la dependencia sea opcional en la configuraci\u00f3n de CI.<\/p>\n<h3>6. Escribir afirmaciones vagas<\/h3>\n<p>Las pruebas deben expresar claramente lo que se est\u00e1 verificando y por qu\u00e9.<\/p>\n<pre><code class=\"language-python\"># \u274c Unclear: what's being tested?\nassert result != None\n\n# \u2705 Clear: specific expectation with context\nassert result.converged is True, \"Solver should converge for well-posed problem\"\n<\/code><\/pre>\n<h3>7. Caminos de codificaci\u00f3n y supuestos ambientales<\/h3>\n<p>Utilice directorios y accesorios temporales en lugar de rutas fijas. El dispositivo <code>tmp_path<\/code> de PyTest proporciona un directorio temporal nuevo para cada prueba.<\/p>\n<h2>Ejemplo pr\u00e1ctico: Probar un solucionador de difusi\u00f3n Fipy<\/h2>\n<p>Pongamos estas estrategias junto con un ejemplo concreto relevante para la audiencia de Matforge.<\/p>\n<pre><code class=\"language-python\"># tests\/test_diffusion.py\nimport pytest\nimport numpy as np\nfrom fipy import Grid1D, CellVariable, DiffusionTerm\n\n@pytest.fixture\ndef simple_1d_grid():\n    \"\"\"Create a uniform 1D grid for diffusion testing.\"\"\"\n    return Grid1D(dx=0.1, nx=50)\n\n@pytest.fixture\ndef steady_state_diffusion(simple_1d_grid):\n    \"\"\"Set up diffusion with Dirichlet boundaries at both ends.\"\"\"\n    mesh = simple_1d_grid\n    var = CellVariable(name=\"concentration\", mesh=mesh, value=0.0)\n    var.constrain(1.0, mesh.facesLeft)\n    var.constrain(0.0, mesh.facesRight)\n    eq = DiffusionTerm(coeff=1.0) == 0\n    return var, eq\n\ndef test_mesh_volume(simple_1d_grid):\n    \"\"\"Total domain length should equal nx * dx.\"\"\"\n    expected_length = 50 * 0.1\n    assert simple_1d_grid.cellVolumes.sum() == pytest.approx(expected_length)\n\ndef test_diffusion_conservation(steady_state_diffusion):\n    \"\"\"For steady diffusion with no sources, total mass should be conserved.\"\"\"\n    var, eq = steady_state_diffusion\n    eq.solve(var, dt=1.0)\n    # With fixed values at boundaries, mass enters from left and exits right\n    # In steady state, flux in should equal flux out\n    left_flux = var.faceValue[simple_1d_grid.facesLeft.value]\n    right_flux = var.faceValue[simple_1d_grid.facesRight.value]\n    # Flux direction: positive means flow to the right\n    assert left_flux &gt; 0  # mass enters from left\n    assert right_flux &lt; 0  # mass exits from right (negative direction)\n    assert abs(left_flux + right_flux) == pytest.approx(0, abs=1e-10)\n\ndef test_diffusion_solution_shape(steady_state_diffusion):\n    \"\"\"Concentration should decrease monotonically from left to right.\"\"\"\n    var, eq = steady_state_diffusion\n    eq.solve(var, dt=1.0)\n    # Steady state should be linear for constant diffusivity\n    x = simple_1d_grid.cellCenters[0]\n    expected = 1.0 - x \/ (50 * 0.1)  # linear from 1 to 0\n    assert var.value == pytest.approx(expected, rel=1e-5)\n\n@pytest.mark.parametrize(\"dx,nx\", [(0.1, 50), (0.05, 100), (0.02, 250)])\ndef test_mesh_independence(dx, nx):\n    \"\"\"Solution should converge as mesh refines.\"\"\"\n    mesh = Grid1D(dx=dx, nx=nx)\n    var = CellVariable(name=\"c\", mesh=mesh, value=0.0)\n    var.constrain(1.0, mesh.facesLeft)\n    var.constrain(0.0, mesh.facesRight)\n    eq = DiffusionTerm(coeff=1.0) == 0\n    eq.solve(var, dt=1.0)\n    # Check at midpoint\n    mid_idx = nx \/\/ 2\n    assert var.value[mid_idx] == pytest.approx(0.5, rel=0.1)\n<\/code><\/pre>\n<p>Este ejemplo demuestra:<\/p>\n<ul>\n<li>Accesorios para la configuraci\u00f3n de prueba reutilizable<\/li>\n<li>Parametrizaci\u00f3n para probar m\u00faltiples resoluciones<\/li>\n<li>Aserciones num\u00e9ricas basadas en la tolerancia<\/li>\n<li>Prueba de principios f\u00edsicos (conservaci\u00f3n, linealidad)<\/li>\n<li>Nombres y afirmaciones de prueba claros y descriptivos<\/li>\n<\/ul>\n<h2>Cuando la prueba unitaria no es suficiente<\/h2>\n<p>Las pruebas unitarias validan los componentes individuales, pero el software de investigaci\u00f3n tambi\u00e9n necesita:<\/p>\n<ul>\n<li><strong>Pruebas de integraci\u00f3n<\/strong>: verifique que varios m\u00f3dulos funcionen juntos correctamente<\/li>\n<li><strong>Pruebas del sistema<\/strong>: Ejecute simulaciones completas de extremo a extremo y compare con las salidas conocidas<\/li>\n<li><strong>Pruebas de rendimiento<\/strong>: garantizar que los algoritmos cumplan con las expectativas de complejidad computacional<\/li>\n<li><strong>Verificaciones de visualizaci\u00f3n<\/strong>: spot Errores de renderizado obvios (comparaci\u00f3n de im\u00e1genes automatizada cuando sea factible)<\/li>\n<\/ul>\n<p>Una estrategia de prueba completa para proyectos de investigaci\u00f3n incluye m\u00faltiples niveles de prueba, con pruebas unitarias que forman la base.<\/p>\n<h2>Lo que recomendamos: Una estrategia de prueba pragm\u00e1tica para proyectos de investigaci\u00f3n<\/h2>\n<p>Basado en la evidencia de las mejores pr\u00e1cticas de software cient\u00edfico, aqu\u00ed est\u00e1 nuestro enfoque recomendado:<\/p>\n<h3>Comience con las pruebas unitarias fundamentales<\/h3>\n<p>Comience escribiendo pruebas para:<\/p>\n<ul>\n<li>Funciones matem\u00e1ticas b\u00e1sicas (funciones especiales, transformaciones de coordenadas)<\/li>\n<li>Utilidades de generaci\u00f3n y manipulaci\u00f3n de mallas<\/li>\n<li>Implementaciones de condiciones de contorno<\/li>\n<li>Rutinas de entrada\/salida de datos (validaci\u00f3n, formato)<\/li>\n<\/ul>\n<p>Estos componentes son estables, tienen comportamientos claros esperados y se reutilizan en muchas simulaciones.<\/p>\n<h3>Adopte pytest.approx como est\u00e1ndar<\/h3>\n<p>Nunca use <code>==<\/code> para resultados de coma flotante. Utilice siempre <code>pytest.approx()<\/code> con las tolerancias apropiadas. Haga de esta una convenci\u00f3n de equipo.<\/p>\n<h3>Usar accesorios extensivamente<\/h3>\n<p>Los accesorios reducen la duplicaci\u00f3n y hacen que las pruebas sean m\u00e1s mantenibles. Crear accesorios para:<\/p>\n<ul>\n<li>Mallas comunes (rejillas de prueba 1D, 2D, 3D)<\/li>\n<li>Configuraciones de condiciones de contorno est\u00e1ndar<\/li>\n<li>soluciones anal\u00edticas conocidas<\/li>\n<li>Gesti\u00f3n de archivos\/directorios temporales<\/li>\n<\/ul>\n<h3>integrar CI temprano<\/h3>\n<p>Configure acciones de GitHub (o similares) antes de que el proyecto crezca. Ejecutar pruebas autom\u00e1ticamente en:<\/p>\n<ul>\n<li>cada empuj\u00f3n<\/li>\n<li>Cada solicitud de extracci\u00f3n<\/li>\n<li>Construcciones nocturnas programadas (para atrapar la deriva ambiental)<\/li>\n<\/ul>\n<h3>Medir y rastrear la cobertura del c\u00f3digo<\/h3>\n<p>Use <code>pytest-cov<\/code> para medir qu\u00e9 partes de su c\u00f3digo se ejercen mediante las pruebas. Apunta a tener al menos un 80% de cobertura en los m\u00f3dulos centrales, pero no te obsesiones m\u00e1s del 100%: el objetivo es la confianza, no una puntuaci\u00f3n perfecta.<\/p>\n<h3>Escribe pruebas cuando corrijas errores<\/h3>\n<p>Cada vez que se informa un error, escriba una prueba que lo reproduzca antes de corregirlo. Esto garantiza que el error no volver\u00e1 a aparecer m\u00e1s tarde.<\/p>\n<h3>Mantenga las pruebas r\u00e1pidas<\/h3>\n<p>Si una prueba toma m\u00e1s de unos pocos segundos, considere:<\/p>\n<ul>\n<li>Uso de problemas de prueba m\u00e1s peque\u00f1os<\/li>\n<li>burl\u00e1ndose de las operaciones caras<\/li>\n<li>Moverlo a un conjunto de pruebas de integraci\u00f3n que se ejecuta con menos frecuencia<\/li>\n<\/ul>\n<h2>Gu\u00edas relacionadas<\/h2>\n<ul>\n<li><a href=\"\/tracking-long-term-technical-debt-in-research-software\/\">seguimiento de la deuda t\u00e9cnica en software de investigaci\u00f3n<\/a> \u2013 Gesti\u00f3n de la deuda de prueba a medida que los proyectos evolucionan<\/li>\n<li><a href=\"\/managing-research-software-through-tickets\/\">administrar el software de investigaci\u00f3n a trav\u00e9s de tickets<\/a> \u2013 usando el seguimiento de problemas para coordinar los esfuerzos de prueba<\/li>\n<li><a href=\"\/reproducibility-and-its-role-in-debugging\/\">Reproducibilidad y su papel en la depuraci\u00f3n<\/a> \u2013 C\u00f3mo las pruebas permiten la depuraci\u00f3n sistem\u00e1tica<\/li>\n<li><a href=\"\/how-to-write-a-clear-and-useful-bug-report\/\">C\u00f3mo escribir un informe de error claro y \u00fatil<\/a> \u2013 proporcionando la informaci\u00f3n necesaria para crear pruebas de regresi\u00f3n<\/li>\n<li><a href=\"\/feature-requests-vs-bug-reports-knowing-the-difference\/\">Solicitudes de funciones frente a informes de errores: Conociendo la diferencia<\/a> \u2013 Clasificaci\u00f3n de problemas que impulsan el desarrollo de pruebas<\/li>\n<\/ul>\n<h2>Conclusi\u00f3n<\/h2>\n<p>La prueba unitaria transforma el software de investigaci\u00f3n de scripts fr\u00e1giles en instrumentos confiables y reproducibles. Si bien la configuraci\u00f3n de pruebas integrales requiere una inversi\u00f3n inicial, el pago viene en un tiempo de depuraci\u00f3n reducido, una mayor confianza en los resultados y una colaboraci\u00f3n m\u00e1s fluida.<\/p>\n<p>El marco PyTest proporciona herramientas poderosas (fixtures, parametrizaci\u00f3n, burlas y <code>approx()<\/code>) que abordan directamente los desaf\u00edos del c\u00f3digo cient\u00edfico: precisi\u00f3n num\u00e9rica, dependencias externas y respuestas correctas desconocidas. Combinadas con la integraci\u00f3n continua, estas pr\u00e1cticas aseguran que las pruebas se ejecuten de manera consistente en todos los entornos.<\/p>\n<p>Recuerde: las pruebas no se trata de lograr la perfecci\u00f3n. Se trata de generar suficiente confianza en su c\u00f3digo para que pueda confiar en sus resultados cuando m\u00e1s importa. Comience con los componentes principales, escriba pruebas que expresen expectativas claras y ampl\u00eden gradualmente la cobertura a medida que crece el proyecto.<\/p>\n<p>Su futuro yo, y cualquiera que herede su c\u00f3digo, se lo agradecer\u00e1.<\/p>\n<h2>Pr\u00f3ximos pasos<\/h2>\n<p>\u00bfListo para agregar pruebas a su proyecto de investigaci\u00f3n?<\/p>\n<ol>\n<li>Instalar PyTest: <code>pip install pytest<\/code><\/li>\n<li>Cree un directorio <code>tests\/<\/code> con un archivo de prueba simple<\/li>\n<li>Escriba una prueba para una funci\u00f3n central usando <code>pytest.approx<\/code><\/li>\n<li>Configure un flujo de trabajo de acciones de GitHub para ejecutar pruebas autom\u00e1ticamente<\/li>\n<li>Ampl\u00ede gradualmente la cobertura a medida que modifica el c\u00f3digo<\/li>\n<\/ol>\n<p>Para obtener ayuda personalizada para implementar estrategias de prueba en su software de investigaci\u00f3n espec\u00edfico, <a href=\"\/\">cont\u00e1ctenos para una consulta<\/a> (visite nuestra p\u00e1gina de inicio para obtener m\u00e1s informaci\u00f3n).<\/p>\n","protected":false,"raw":"<p>Las pruebas unitarias no son negociables para software cient\u00edfico de confianza. A diferencia de las aplicaciones comerciales, el c\u00f3digo de investigaci\u00f3n a menudo carece de pruebas formales, lo que conduce a resultados irreproducibles y un esfuerzo desperdiciado. Esta gu\u00eda cubre las estrategias de PyTest espec\u00edficamente para proyectos cient\u00edficos de Python: manejo de precisi\u00f3n num\u00e9rica con <code>pytest.approx<\/code>, aislando dependencias externas con simulacro, uso de accesorios y parametrizaci\u00f3n de manera eficiente e integrando pruebas en canalizaciones de integraci\u00f3n continua. Aprender\u00e1 cu\u00e1ndo usar la validaci\u00f3n de caja negra con los resultados publicados y c\u00f3mo dise\u00f1ar pruebas que sobrevivan a la evoluci\u00f3n del c\u00f3digo sin volverse fr\u00e1gil.<\/p>\n<h2>Por qu\u00e9 es importante la prueba unitaria en el software de investigaci\u00f3n<\/h2>\n<p>El software cient\u00edfico existe en un espacio desafiante. Debe ser lo suficientemente flexible como para explorar nuevas hip\u00f3tesis, pero lo suficientemente confiable como para que los resultados publicados puedan reproducirse meses o a\u00f1os despu\u00e9s. A diferencia del software comercial con especificaciones claras, el c\u00f3digo de investigaci\u00f3n a menudo evoluciona junto con los experimentos, y los requisitos cambian a medida que surgen nuevos descubrimientos.<\/p>\n<p>Las consecuencias de las pruebas inadecuadas en la investigaci\u00f3n son graves:<\/p>\n<ul>\n<li><strong>Resultados irreproducibles<\/strong>: diferentes investigadores obtienen diferentes resultados del mismo c\u00f3digo<\/li>\n<li><strong>Errores silenciosos<\/strong>: errores num\u00e9ricos que parecen peque\u00f1os individualmente compuestos en inexactitudes significativas<\/li>\n<li><strong>P\u00e9rdida de conocimiento<\/strong>: cuando los desarrolladores originales se van, las pruebas sirven como documentaci\u00f3n ejecutable<\/li>\n<li><strong>Esfuerzo desperdiciado<\/strong>: la depuraci\u00f3n se convierte en una caza de detectives en lugar de un proceso sistem\u00e1tico<\/li>\n<\/ul>\n<p>Las pruebas unitarias abordan estos problemas validando componentes peque\u00f1os y aislados de su c\u00f3digo. Cada prueba verifica que una funci\u00f3n o clase espec\u00edfica se comporta como se espera dadas las entradas definidas. Cuando las pruebas pasan de manera consistente entre entornos, tiene evidencia de que su c\u00f3digo produce resultados confiables.<\/p>\n<p>Pero probar el c\u00f3digo cient\u00edfico presenta desaf\u00edos \u00fanicos que los enfoques de prueba de software est\u00e1ndar no abordan completamente.<\/p>\n<h2>Desaf\u00edos \u00fanicos de probar el c\u00f3digo cient\u00edfico<\/h2>\n<p>El c\u00f3digo cient\u00edfico y num\u00e9rico difiere de las aplicaciones comerciales t\u00edpicas de varias maneras que afectan la estrategia de prueba.<\/p>\n<h3>Precisi\u00f3n num\u00e9rica y errores de punto flotante<\/h3>\n<p>La aritm\u00e9tica de punto flotante es inherentemente imprecisa. Debido a c\u00f3mo los equipos representan n\u00fameros decimales, <code>0.1 + 0.2<\/code> no es exactamente <code>0.3<\/code> en la representaci\u00f3n de punto flotante binario. En simulaciones cient\u00edficas que involucran miles o millones de operaciones, estos peque\u00f1os errores se acumulan.<\/p>\n<p>Una prueba ingenua que utiliza una igualdad exacta (<code>==<\/code>) fallar\u00e1 de forma intermitente o en diferentes hardware:<\/p>\n<pre><code class=\"language-python\">def test_numerical_computation():\n    result = complex_simulation()  # returns 1.0000000000000002\n    assert result == 1.0  # FAILS! Even though the difference is negligible\n<\/code><\/pre>\n<p>La soluci\u00f3n es utilizar comparaciones basadas en la tolerancia. PyTest proporciona <code>pytest.approx()<\/code> para este prop\u00f3sito:<\/p>\n<pre><code class=\"language-python\">def test_numerical_computation():\n    result = complex_simulation()\n    assert result == pytest.approx(1.0, rel=1e-9, abs=1e-12)\n<\/code><\/pre>\n<p><code>rel<\/code> (tolerancia relativa) es \u00fatil para valores de cualquier magnitud; <code>abs<\/code> (tolerancia absoluta) maneja casos en los que el valor esperado es cercano a cero. Los valores predeterminados son <code>rel=1e-6<\/code> y <code>abs=1e-12<\/code>, pero el c\u00f3digo cient\u00edfico a menudo requiere tolerancias m\u00e1s estrictas.<\/p>\n<h3>Respuestas \"correctas\" desconocidas<\/h3>\n<p>En muchos escenarios de investigaci\u00f3n, no tiene una salida correcta conocida. La simulaci\u00f3n podr\u00eda estar explorando territorio desconocido. \u00bfC\u00f3mo se prueba el c\u00f3digo cuando no sabe cu\u00e1l deber\u00eda ser la respuesta?<\/p>\n<p>Varias estrategias funcionan:<\/p>\n<ol>\n<li><strong>Operaciones inversas<\/strong>: Si su c\u00f3digo calcula <code>B = f(A)<\/code>, tambi\u00e9n pruebe que <code>A \u2248 f\u207b\u00b9(B)<\/code>.<\/li>\n<li><strong>Leyes de conservaci\u00f3n<\/strong>: para simulaciones f\u00edsicas, verifique que la masa, la energ\u00eda o el impulso se conserven dentro de la tolerancia.<\/li>\n<li><strong>Casos limitantes<\/strong>: Comportamiento de prueba en l\u00edmites simplificados donde existen soluciones anal\u00edticas.<\/li>\n<li><strong>Pruebas de regresi\u00f3n<\/strong>: Almacene los resultados de una ejecuci\u00f3n de confianza y detecte cambios inesperados.<\/li>\n<\/ol>\n<h3>Dependencias externas y c\u00e1lculo pesado<\/h3>\n<p>El c\u00f3digo cient\u00edfico a menudo depende de:<\/p>\n<ul>\n<li>Grandes conjuntos de datos (terabytes de entrada de simulaci\u00f3n)<\/li>\n<li>Solvers o Bibliotecas externas (Paquetes HPC, Bibliotecas Fortran)<\/li>\n<li>E\/S de archivo con formatos complejos<\/li>\n<li>Conexiones de base de datos o API<\/li>\n<\/ul>\n<p>Ejecutar el sistema completo en cada prueba unitaria no es pr\u00e1ctico. Necesitas aislamiento.<\/p>\n<h2>Caracter\u00edsticas de PyTest que resuelven problemas de prueba de investigaci\u00f3n<\/h2>\n<p>PyTest proporciona varias caracter\u00edsticas que son particularmente valiosas para el c\u00f3digo cient\u00edfico.<\/p>\n<h3>Accesorios para la instalaci\u00f3n y el desmontaje<\/h3>\n<p>Los accesorios encapsulan el c\u00f3digo de instalaci\u00f3n que se ejecuta antes de las pruebas. Para las pruebas cient\u00edficas, los accesorios pueden:<\/p>\n<ul>\n<li>Crear datos o mallas de prueba temporales<\/li>\n<li>Inicializar objetos de simulaci\u00f3n con par\u00e1metros conocidos<\/li>\n<li>Limpiar archivos temporales despu\u00e9s de las pruebas<\/li>\n<li>Proporcionar configuraciones de prueba reutilizables<\/li>\n<\/ul>\n<pre><code class=\"language-python\">import pytest\nimport tempfile\nimport numpy as np\n\n@pytest.fixture\ndef simple_mesh():\n    \"\"\"Create a small 1D mesh for testing.\"\"\"\n    from fipy import Grid1D\n    return Grid1D(dx=0.1, nx=10)\n\n@pytest.fixture\ndef diffusion_solver(simple_mesh):\n    \"\"\"Set up a diffusion solver on the test mesh.\"\"\"\n    from fipy import CellVariable, DiffusionTerm\n    var = CellVariable(name=\"concentration\", mesh=simple_mesh, value=1.0)\n    eq = DiffusionTerm(coeff=1.0) == 0\n    return var, eq\n<\/code><\/pre>\n<h3>Parametrizaci\u00f3n para m\u00faltiples escenarios<\/h3>\n<p>En lugar de escribir funciones de prueba separadas para casos similares, use <code>@pytest.mark.parametrize<\/code> para ejecutar la misma l\u00f3gica de prueba con diferentes entradas.<\/p>\n<pre><code class=\"language-python\">@pytest.mark.parametrize(\"dx,nx,expected_volume\", [\n    (0.1, 10, 1.0),\n    (0.01, 100, 1.0),\n    (0.001, 1000, 1.0),\n])\ndef test_mesh_volume(dx, nx, expected_volume):\n    \"\"\"Test that mesh volume matches domain size.\"\"\"\n    mesh = Grid1D(dx=dx, nx=nx)\n    assert mesh.cellVolumes.sum() == pytest.approx(expected_volume)\n<\/code><\/pre>\n<p>La parametrizaci\u00f3n es especialmente \u00fatil para:<\/p>\n<ul>\n<li>Casos de prueba de borde (valores cero, n\u00fameros muy peque\u00f1os\/grandes)<\/li>\n<li>Verificaci\u00f3n del comportamiento en diferentes resoluciones de malla<\/li>\n<li>Validaci\u00f3n de varios tipos de condiciones de contorno<\/li>\n<li>Comprobaci\u00f3n de varios valores de propiedad del material<\/li>\n<\/ul>\n<p>Para el c\u00f3digo de investigaci\u00f3n, puede parametrizar contra resultados de referencia conocidos de art\u00edculos publicados.<\/p>\n<h3>Burlarse de las dependencias externas<\/h3>\n<p>La burla reemplaza a las dependencias reales con falsificaciones controladas. Esto a\u00edsla la unidad bajo prueba y hace que las pruebas sean m\u00e1s r\u00e1pidas y confiables.<\/p>\n<p><strong>Cu\u00e1ndo simular en c\u00f3digo cient\u00edfico:<\/strong><\/p>\n<ul>\n<li>Archivos de datos externos: reemplace los grandes conjuntos de datos con datos sint\u00e9ticos m\u00ednimos que ejercen las mismas rutas de c\u00f3digo<\/li>\n<li>Solvers HPC: simulacros costosas de Fortran con implementaciones de Python puras que devuelven resultados conocidos<\/li>\n<li>API de red: servicios remotos de stub que proporcionan par\u00e1metros o configuraci\u00f3n<\/li>\n<li>Generadores de n\u00fameros aleatorios: sembrarlos para producir secuencias deterministas<\/li>\n<\/ul>\n<pre><code class=\"language-python\">from unittest.mock import patch, MagicMock\n\ndef test_simulation_with_external_data():\n    # Mock the data loading function to return small, known data\n    with patch('mycode.load_large_dataset') as mock_load:\n        mock_load.return_value = np.array([1.0, 2.0, 3.0])\n        result = run_simulation()\n        assert result.converged\n<\/code><\/pre>\n<p><strong>Directriz importante<\/strong>: simula las dependencias de su propio c\u00f3digo, no las bibliotecas de terceros que no controla. Sigue el principio \"No te burles de lo que no tienes\".<\/p>\n<h3>Usando <code>pytest.approx<\/code> para comparaciones num\u00e9ricas<\/h3>\n<p>Las comparaciones de punto flotante deben tener en cuenta los errores de redondeo. El objeto <code>approx<\/code> de PyTest maneja esto de forma elegante:<\/p>\n<pre><code class=\"language-python\">def test_diffusion_solution():\n    \"\"\"Test that diffusion reaches expected steady state.\"\"\"\n    concentration = solve_diffusion(time=100.0)\n    expected = 0.5  # analytical steady state for this boundary condition\n    assert concentration.mean() == pytest.approx(expected, rel=1e-6)\n<\/code><\/pre>\n<p>Tambi\u00e9n puede usar <code>approx<\/code> con matrices:<\/p>\n<pre><code class=\"language-python\">def test_array_computation():\n    result = compute_field()\n    expected = np.array([1.0, 2.0, 3.0])\n    assert result == pytest.approx(expected)\n<\/code><\/pre>\n<p>Para el c\u00f3digo cient\u00edfico, elija tolerancias basadas en:<\/p>\n<ul>\n<li>La precisi\u00f3n num\u00e9rica de sus m\u00e9todos (por ejemplo, la diferencia finita de segundo orden tiene error de truncamiento O(dx\u00b2))<\/li>\n<li>La precisi\u00f3n requerida por su aplicaci\u00f3n (tolerancia de ingenier\u00eda frente a investigaci\u00f3n exploratoria)<\/li>\n<\/ul>\n<h2>Desarrollo basado en pruebas para proyectos de investigaci\u00f3n<\/h2>\n<p>El desarrollo basado en pruebas (TDD) sigue un ciclo simple: escriba una prueba defectuosa, luego escriba un c\u00f3digo m\u00ednimo para que pase, luego refactorice. Si bien TDD est\u00e1 bien establecido en el software comercial, los proyectos de investigaci\u00f3n a menudo se resisten debido a las limitaciones de tiempo percibidas.<\/p>\n<p>La realidad: TDD ahorra tiempo en la investigaci\u00f3n al detectar errores antes de que se propaguen a trav\u00e9s de experimentos. Las pruebas de escritura primero lo obligan a aclarar la interfaz y el comportamiento esperado de cada funci\u00f3n antes de la implementaci\u00f3n.<\/p>\n<p><strong>TDD adaptado para la exploraci\u00f3n cient\u00edfica:<\/strong><\/p>\n<ol>\n<li>Comience con un modelo o algoritmo simple que entienda anal\u00edticamente<\/li>\n<li>Escribir pruebas que validen contra resultados conocidos (soluciones anal\u00edticas, casos limitantes)<\/li>\n<li>Implementar el c\u00f3digo para pasar esas pruebas<\/li>\n<li>Extender el modelo de forma incremental, agregando pruebas para cada nueva capacidad<\/li>\n<li>Cuando descubra un error, escriba una prueba que lo reproduzca primero, luego corrija<\/li>\n<\/ol>\n<p>TDD funciona bien para:<\/p>\n<ul>\n<li>Funciones de utilidad (generaci\u00f3n de malla, transformaciones de coordenadas)<\/li>\n<li>Operaciones Matem\u00e1ticas (Manipulaciones de Matriz, Funciones Especiales)<\/li>\n<li>Pipelines de procesamiento de datos (an\u00e1lisis, filtrado, normalizaci\u00f3n)<\/li>\n<li>Validaci\u00f3n de configuraci\u00f3n<\/li>\n<\/ul>\n<p>TDD es menos adecuado para:<\/p>\n<ul>\n<li>C\u00f3digo altamente exploratorio donde la interfaz en s\u00ed es incierta<\/li>\n<li>Scripts \u00fanicos que no se reutilizar\u00e1n<\/li>\n<li>C\u00f3digo que depende de los recursos externos a\u00fan no disponibles<\/li>\n<\/ul>\n<p>En la pr\u00e1ctica, un enfoque h\u00edbrido funciona mejor: escribir pruebas para componentes b\u00e1sicos y estables; Utilice pruebas de integraci\u00f3n m\u00e1s ligeras para secciones experimentales.<\/p>\n<h2>Organizaci\u00f3n de pruebas para proyectos cient\u00edficos<\/h2>\n<p>\u00bfD\u00f3nde deber\u00edan vivir los archivos de prueba? PyTest ofrece flexibilidad:<\/p>\n<pre><code>my_research_project\/\n\u251c\u2500\u2500 src\/\n\u2502   \u2514\u2500\u2500 mypackage\/\n\u2502       \u251c\u2500\u2500 __init__.py\n\u2502       \u251c\u2500\u2500 solver.py\n\u2502       \u2514\u2500\u2500 mesh.py\n\u251c\u2500\u2500 tests\/\n\u2502   \u251c\u2500\u2500 __init__.py\n\u2502   \u251c\u2500\u2500 test_solver.py\n\u2502   \u251c\u2500\u2500 test_mesh.py\n\u2502   \u2514\u2500\u2500 conftest.py  # shared fixtures\n\u251c\u2500\u2500 data\/\n\u2502   \u2514\u2500\u2500 reference_results\/  # stored outputs for regression tests\n\u251c\u2500\u2500 .github\/\n\u2502   \u2514\u2500\u2500 workflows\/\n\u2502       \u2514\u2500\u2500 ci.yml  # GitHub Actions CI configuration\n\u251c\u2500\u2500 pyproject.toml\n\u2514\u2500\u2500 README.md\n<\/code><\/pre>\n<p><strong>Convenciones clave:<\/strong><\/p>\n<ul>\n<li>Mantenga las pruebas en un directorio separado <code>tests\/<\/code> paralelo a <code>src\/<\/code> (o <code>lib\/<\/code>)<\/li>\n<li>Nombre de archivo de prueba <code>test_*.py<\/code> o <code>*_test.py<\/code><\/li>\n<li>Funciones de prueba de nombre <code>test_*()<\/code> Para permitir el descubrimiento autom\u00e1tico de PyTest<\/li>\n<li>Use <code>conftest.py<\/code> para accesorios compartidos en varios archivos de prueba<\/li>\n<\/ul>\n<p>Para proyectos basados en FIPY, pruebas de estructura para que coincidan con la jerarqu\u00eda de m\u00f3dulos:<\/p>\n<pre><code>fipy_project\/\n\u251c\u2500\u2500 fipy\/\n\u2502   \u251c\u2500\u2500 meshes\/\n\u2502   \u2502   \u2514\u2500\u2500 grid1d.py\n\u2502   \u2514\u2500\u2500 terms\/\n\u2502       \u2514\u2500\u2500 diffusion.py\n\u251c\u2500\u2500 tests\/\n\u2502   \u251c\u2500\u2500 meshes\/\n\u2502   \u2502   \u2514\u2500\u2500 test_grid1d.py\n\u2502   \u2514\u2500\u2500 terms\/\n\u2502       \u2514\u2500\u2500 test_diffusion.py\n<\/code><\/pre>\n<h2>Integraci\u00f3n con integraci\u00f3n continua<\/h2>\n<p>Las pruebas unitarias solo proporcionan valor si se ejecutan de manera consistente. La integraci\u00f3n continua (CI) automatiza la ejecuci\u00f3n de la prueba cada vez que cambia el c\u00f3digo.<\/p>\n<p>Acciones de GitHub proporciona una configuraci\u00f3n de CI sencilla para proyectos de Python:<\/p>\n<pre><code class=\"language-yaml\"># .github\/workflows\/ci.yml\nname: CI\n\non: [push, pull_request]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [\"3.9\", \"3.10\", \"3.11\"]\n\n    steps:\n    - uses: actions\/checkout@v3\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions\/setup-python@v4\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install -e .[test]\n    - name: Run tests with pytest\n      run: |\n        pytest --cov=src --cov-report=xml --cov-report=html\n    - name: Upload coverage\n      uses: codecov\/codecov-action@v3\n<\/code><\/pre>\n<p>CI asegura:<\/p>\n<ul>\n<li>Las pruebas pasan en m\u00faltiples plataformas (Linux, macOS, Windows)<\/li>\n<li>Las pruebas pasan en varias versiones de Python<\/li>\n<li>La cobertura del c\u00f3digo se rastrea a lo largo del tiempo<\/li>\n<li>Las solicitudes de extracci\u00f3n se validan antes de fusionar<\/li>\n<\/ul>\n<p>Para el software de investigaci\u00f3n, considere agregar:<\/p>\n<ul>\n<li>Pruebas que se ejecutan con diferentes versiones de dependencias clave (numpy, scipy, fipy)<\/li>\n<li>Comprobaciones de regresi\u00f3n de rendimiento (asegurar que los algoritmos no se ralenticen)<\/li>\n<li>Las compilaciones de documentaci\u00f3n para verificar los ejemplos a\u00fan funcionan<\/li>\n<\/ul>\n<h2>Errores comunes y c\u00f3mo evitarlos<\/h2>\n<p>Con base en las mejores pr\u00e1cticas de investigaci\u00f3n y de la industria, aqu\u00ed est\u00e1n los errores de prueba unitarias m\u00e1s comunes en el c\u00f3digo cient\u00edfico:<\/p>\n<h3>1. Prueba de detalles de implementaci\u00f3n en lugar de comportamiento<\/h3>\n<p>Probar la implementaci\u00f3n interna hace que las pruebas sean fr\u00e1giles. Cuando refactoriza el c\u00f3digo, las pruebas a\u00fan deben pasar si el comportamiento externo es correcto.<\/p>\n<pre><code class=\"language-python\"># \u274c Bad: tests internal state\ndef test_algorithm_updates_counter():\n    obj = MyAlgorithm()\n    obj.step()\n    assert obj.counter == 1  # fragile if counter implementation changes\n\n# \u2705 Better: tests observable outcome\ndef test_algorithm_produces_correct_result():\n    obj = MyAlgorithm()\n    result = obj.run()\n    assert result == expected\n<\/code><\/pre>\n<h3>2. Ignorar la tolerancia num\u00e9rica<\/h3>\n<p>Las comparaciones exactas en los resultados de coma flotante provocan pruebas descamadas que fallan aleatoriamente o en diferentes hardware. Utilice siempre <code>pytest.approx()<\/code> o afirmaciones similares basadas en tolerancia para salidas num\u00e9ricas.<\/p>\n<h3>3. Escribir pruebas lentas<\/h3>\n<p>Las pruebas unitarias deben ejecutarse r\u00e1pidamente (milisegundos, no segundos). Si una prueba es lenta:<\/p>\n<ul>\n<li>no se ejecutar\u00e1 con la frecuencia suficiente<\/li>\n<li>Los desarrolladores omitir\u00e1n la ejecuci\u00f3n de la suite de pruebas completa<\/li>\n<li>IC se vuelve caro y lento<\/li>\n<\/ul>\n<p><strong>Soluciones:<\/strong><\/p>\n<ul>\n<li>Utilice peque\u00f1os conjuntos de datos sint\u00e9ticos en lugar de grandes reales<\/li>\n<li>simulacros de c\u00e1lculos externos caros<\/li>\n<li>Separe las pruebas de integraci\u00f3n lenta de las pruebas unitarias r\u00e1pidas<\/li>\n<li>Use la parametrizaci\u00f3n sabiamente: no ejecute miles de variaciones en cada pase de prueba<\/li>\n<\/ul>\n<h3>4. No aislar las pruebas<\/h3>\n<p>Las pruebas no deben depender unas de otras ni del estado global. Cada prueba debe:<\/p>\n<ul>\n<li>Cree sus propios datos de prueba (utilice accesorios)<\/li>\n<li>Limpiar despu\u00e9s de s\u00ed mismo<\/li>\n<li>no depender de la orden de ejecuci\u00f3n<\/li>\n<\/ul>\n<pre><code class=\"language-python\"># \u274c Bad: shared mutable state\nresults = []\n\ndef test_first():\n    results.append(1)\n\ndef test_second():\n    assert results == [1]  # fails if tests run in wrong order\n\n# \u2705 Better: independent tests\ndef test_first():\n    result = compute_something()\n    assert result == 1\n\ndef test_second():\n    result = compute_something_else()\n    assert result == 2\n<\/code><\/pre>\n<h3>5. Saltarse las pruebas sin una buena raz\u00f3n<\/h3>\n<p><code>@pytest.mark.skip<\/code> se debe usar con moderaci\u00f3n. Si se omite una prueba porque el entorno carece de algo, use <code>pytest.importorskip()<\/code> a nivel de m\u00f3dulo o haga que la dependencia sea opcional en la configuraci\u00f3n de CI.<\/p>\n<h3>6. Escribir afirmaciones vagas<\/h3>\n<p>Las pruebas deben expresar claramente lo que se est\u00e1 verificando y por qu\u00e9.<\/p>\n<pre><code class=\"language-python\"># \u274c Unclear: what's being tested?\nassert result != None\n\n# \u2705 Clear: specific expectation with context\nassert result.converged is True, \"Solver should converge for well-posed problem\"\n<\/code><\/pre>\n<h3>7. Caminos de codificaci\u00f3n y supuestos ambientales<\/h3>\n<p>Utilice directorios y accesorios temporales en lugar de rutas fijas. El dispositivo <code>tmp_path<\/code> de PyTest proporciona un directorio temporal nuevo para cada prueba.<\/p>\n<h2>Ejemplo pr\u00e1ctico: Probar un solucionador de difusi\u00f3n Fipy<\/h2>\n<p>Pongamos estas estrategias junto con un ejemplo concreto relevante para la audiencia de Matforge.<\/p>\n<pre><code class=\"language-python\"># tests\/test_diffusion.py\nimport pytest\nimport numpy as np\nfrom fipy import Grid1D, CellVariable, DiffusionTerm\n\n@pytest.fixture\ndef simple_1d_grid():\n    \"\"\"Create a uniform 1D grid for diffusion testing.\"\"\"\n    return Grid1D(dx=0.1, nx=50)\n\n@pytest.fixture\ndef steady_state_diffusion(simple_1d_grid):\n    \"\"\"Set up diffusion with Dirichlet boundaries at both ends.\"\"\"\n    mesh = simple_1d_grid\n    var = CellVariable(name=\"concentration\", mesh=mesh, value=0.0)\n    var.constrain(1.0, mesh.facesLeft)\n    var.constrain(0.0, mesh.facesRight)\n    eq = DiffusionTerm(coeff=1.0) == 0\n    return var, eq\n\ndef test_mesh_volume(simple_1d_grid):\n    \"\"\"Total domain length should equal nx * dx.\"\"\"\n    expected_length = 50 * 0.1\n    assert simple_1d_grid.cellVolumes.sum() == pytest.approx(expected_length)\n\ndef test_diffusion_conservation(steady_state_diffusion):\n    \"\"\"For steady diffusion with no sources, total mass should be conserved.\"\"\"\n    var, eq = steady_state_diffusion\n    eq.solve(var, dt=1.0)\n    # With fixed values at boundaries, mass enters from left and exits right\n    # In steady state, flux in should equal flux out\n    left_flux = var.faceValue[simple_1d_grid.facesLeft.value]\n    right_flux = var.faceValue[simple_1d_grid.facesRight.value]\n    # Flux direction: positive means flow to the right\n    assert left_flux &gt; 0  # mass enters from left\n    assert right_flux &lt; 0  # mass exits from right (negative direction)\n    assert abs(left_flux + right_flux) == pytest.approx(0, abs=1e-10)\n\ndef test_diffusion_solution_shape(steady_state_diffusion):\n    \"\"\"Concentration should decrease monotonically from left to right.\"\"\"\n    var, eq = steady_state_diffusion\n    eq.solve(var, dt=1.0)\n    # Steady state should be linear for constant diffusivity\n    x = simple_1d_grid.cellCenters[0]\n    expected = 1.0 - x \/ (50 * 0.1)  # linear from 1 to 0\n    assert var.value == pytest.approx(expected, rel=1e-5)\n\n@pytest.mark.parametrize(\"dx,nx\", [(0.1, 50), (0.05, 100), (0.02, 250)])\ndef test_mesh_independence(dx, nx):\n    \"\"\"Solution should converge as mesh refines.\"\"\"\n    mesh = Grid1D(dx=dx, nx=nx)\n    var = CellVariable(name=\"c\", mesh=mesh, value=0.0)\n    var.constrain(1.0, mesh.facesLeft)\n    var.constrain(0.0, mesh.facesRight)\n    eq = DiffusionTerm(coeff=1.0) == 0\n    eq.solve(var, dt=1.0)\n    # Check at midpoint\n    mid_idx = nx \/\/ 2\n    assert var.value[mid_idx] == pytest.approx(0.5, rel=0.1)\n<\/code><\/pre>\n<p>Este ejemplo demuestra:<\/p>\n<ul>\n<li>Accesorios para la configuraci\u00f3n de prueba reutilizable<\/li>\n<li>Parametrizaci\u00f3n para probar m\u00faltiples resoluciones<\/li>\n<li>Aserciones num\u00e9ricas basadas en la tolerancia<\/li>\n<li>Prueba de principios f\u00edsicos (conservaci\u00f3n, linealidad)<\/li>\n<li>Nombres y afirmaciones de prueba claros y descriptivos<\/li>\n<\/ul>\n<h2>Cuando la prueba unitaria no es suficiente<\/h2>\n<p>Las pruebas unitarias validan los componentes individuales, pero el software de investigaci\u00f3n tambi\u00e9n necesita:<\/p>\n<ul>\n<li><strong>Pruebas de integraci\u00f3n<\/strong>: verifique que varios m\u00f3dulos funcionen juntos correctamente<\/li>\n<li><strong>Pruebas del sistema<\/strong>: Ejecute simulaciones completas de extremo a extremo y compare con las salidas conocidas<\/li>\n<li><strong>Pruebas de rendimiento<\/strong>: garantizar que los algoritmos cumplan con las expectativas de complejidad computacional<\/li>\n<li><strong>Verificaciones de visualizaci\u00f3n<\/strong>: spot Errores de renderizado obvios (comparaci\u00f3n de im\u00e1genes automatizada cuando sea factible)<\/li>\n<\/ul>\n<p>Una estrategia de prueba completa para proyectos de investigaci\u00f3n incluye m\u00faltiples niveles de prueba, con pruebas unitarias que forman la base.<\/p>\n<h2>Lo que recomendamos: Una estrategia de prueba pragm\u00e1tica para proyectos de investigaci\u00f3n<\/h2>\n<p>Basado en la evidencia de las mejores pr\u00e1cticas de software cient\u00edfico, aqu\u00ed est\u00e1 nuestro enfoque recomendado:<\/p>\n<h3>Comience con las pruebas unitarias fundamentales<\/h3>\n<p>Comience escribiendo pruebas para:<\/p>\n<ul>\n<li>Funciones matem\u00e1ticas b\u00e1sicas (funciones especiales, transformaciones de coordenadas)<\/li>\n<li>Utilidades de generaci\u00f3n y manipulaci\u00f3n de mallas<\/li>\n<li>Implementaciones de condiciones de contorno<\/li>\n<li>Rutinas de entrada\/salida de datos (validaci\u00f3n, formato)<\/li>\n<\/ul>\n<p>Estos componentes son estables, tienen comportamientos claros esperados y se reutilizan en muchas simulaciones.<\/p>\n<h3>Adopte pytest.approx como est\u00e1ndar<\/h3>\n<p>Nunca use <code>==<\/code> para resultados de coma flotante. Utilice siempre <code>pytest.approx()<\/code> con las tolerancias apropiadas. Haga de esta una convenci\u00f3n de equipo.<\/p>\n<h3>Usar accesorios extensivamente<\/h3>\n<p>Los accesorios reducen la duplicaci\u00f3n y hacen que las pruebas sean m\u00e1s mantenibles. Crear accesorios para:<\/p>\n<ul>\n<li>Mallas comunes (rejillas de prueba 1D, 2D, 3D)<\/li>\n<li>Configuraciones de condiciones de contorno est\u00e1ndar<\/li>\n<li>soluciones anal\u00edticas conocidas<\/li>\n<li>Gesti\u00f3n de archivos\/directorios temporales<\/li>\n<\/ul>\n<h3>integrar CI temprano<\/h3>\n<p>Configure acciones de GitHub (o similares) antes de que el proyecto crezca. Ejecutar pruebas autom\u00e1ticamente en:<\/p>\n<ul>\n<li>cada empuj\u00f3n<\/li>\n<li>Cada solicitud de extracci\u00f3n<\/li>\n<li>Construcciones nocturnas programadas (para atrapar la deriva ambiental)<\/li>\n<\/ul>\n<h3>Medir y rastrear la cobertura del c\u00f3digo<\/h3>\n<p>Use <code>pytest-cov<\/code> para medir qu\u00e9 partes de su c\u00f3digo se ejercen mediante las pruebas. Apunta a tener al menos un 80% de cobertura en los m\u00f3dulos centrales, pero no te obsesiones m\u00e1s del 100%: el objetivo es la confianza, no una puntuaci\u00f3n perfecta.<\/p>\n<h3>Escribe pruebas cuando corrijas errores<\/h3>\n<p>Cada vez que se informa un error, escriba una prueba que lo reproduzca antes de corregirlo. Esto garantiza que el error no volver\u00e1 a aparecer m\u00e1s tarde.<\/p>\n<h3>Mantenga las pruebas r\u00e1pidas<\/h3>\n<p>Si una prueba toma m\u00e1s de unos pocos segundos, considere:<\/p>\n<ul>\n<li>Uso de problemas de prueba m\u00e1s peque\u00f1os<\/li>\n<li>burl\u00e1ndose de las operaciones caras<\/li>\n<li>Moverlo a un conjunto de pruebas de integraci\u00f3n que se ejecuta con menos frecuencia<\/li>\n<\/ul>\n<h2>Gu\u00edas relacionadas<\/h2>\n<ul>\n<li><a href=\"\/tracking-long-term-technical-debt-in-research-software\/\">seguimiento de la deuda t\u00e9cnica en software de investigaci\u00f3n<\/a> \u2013 Gesti\u00f3n de la deuda de prueba a medida que los proyectos evolucionan<\/li>\n<li><a href=\"\/managing-research-software-through-tickets\/\">administrar el software de investigaci\u00f3n a trav\u00e9s de tickets<\/a> \u2013 usando el seguimiento de problemas para coordinar los esfuerzos de prueba<\/li>\n<li><a href=\"\/reproducibility-and-its-role-in-debugging\/\">Reproducibilidad y su papel en la depuraci\u00f3n<\/a> \u2013 C\u00f3mo las pruebas permiten la depuraci\u00f3n sistem\u00e1tica<\/li>\n<li><a href=\"\/how-to-write-a-clear-and-useful-bug-report\/\">C\u00f3mo escribir un informe de error claro y \u00fatil<\/a> \u2013 proporcionando la informaci\u00f3n necesaria para crear pruebas de regresi\u00f3n<\/li>\n<li><a href=\"\/feature-requests-vs-bug-reports-knowing-the-difference\/\">Solicitudes de funciones frente a informes de errores: Conociendo la diferencia<\/a> \u2013 Clasificaci\u00f3n de problemas que impulsan el desarrollo de pruebas<\/li>\n<\/ul>\n<h2>Conclusi\u00f3n<\/h2>\n<p>La prueba unitaria transforma el software de investigaci\u00f3n de scripts fr\u00e1giles en instrumentos confiables y reproducibles. Si bien la configuraci\u00f3n de pruebas integrales requiere una inversi\u00f3n inicial, el pago viene en un tiempo de depuraci\u00f3n reducido, una mayor confianza en los resultados y una colaboraci\u00f3n m\u00e1s fluida.<\/p>\n<p>El marco PyTest proporciona herramientas poderosas (fixtures, parametrizaci\u00f3n, burlas y <code>approx()<\/code>) que abordan directamente los desaf\u00edos del c\u00f3digo cient\u00edfico: precisi\u00f3n num\u00e9rica, dependencias externas y respuestas correctas desconocidas. Combinadas con la integraci\u00f3n continua, estas pr\u00e1cticas aseguran que las pruebas se ejecuten de manera consistente en todos los entornos.<\/p>\n<p>Recuerde: las pruebas no se trata de lograr la perfecci\u00f3n. Se trata de generar suficiente confianza en su c\u00f3digo para que pueda confiar en sus resultados cuando m\u00e1s importa. Comience con los componentes principales, escriba pruebas que expresen expectativas claras y ampl\u00eden gradualmente la cobertura a medida que crece el proyecto.<\/p>\n<p>Su futuro yo, y cualquiera que herede su c\u00f3digo, se lo agradecer\u00e1.<\/p>\n<h2>Pr\u00f3ximos pasos<\/h2>\n<p>\u00bfListo para agregar pruebas a su proyecto de investigaci\u00f3n?<\/p>\n<ol>\n<li>Instalar PyTest: <code>pip install pytest<\/code><\/li>\n<li>Cree un directorio <code>tests\/<\/code> con un archivo de prueba simple<\/li>\n<li>Escriba una prueba para una funci\u00f3n central usando <code>pytest.approx<\/code><\/li>\n<li>Configure un flujo de trabajo de acciones de GitHub para ejecutar pruebas autom\u00e1ticamente<\/li>\n<li>Ampl\u00ede gradualmente la cobertura a medida que modifica el c\u00f3digo<\/li>\n<\/ol>\n<p>Para obtener ayuda personalizada para implementar estrategias de prueba en su software de investigaci\u00f3n espec\u00edfico, <a href=\"\/\">cont\u00e1ctenos para una consulta<\/a> (visite nuestra p\u00e1gina de inicio para obtener m\u00e1s informaci\u00f3n).<\/p>\n"},"excerpt":{"rendered":"<p><span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Reading Time: <\/span> <span class=\"rt-time\"> 11<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span>Las pruebas unitarias no son negociables para software cient\u00edfico de confianza. A diferencia de las aplicaciones comerciales, el c\u00f3digo de investigaci\u00f3n a menudo carece de pruebas formales, lo que conduce a resultados irreproducibles y un esfuerzo desperdiciado. Esta gu\u00eda cubre las estrategias de PyTest espec\u00edficamente para proyectos cient\u00edficos de Python: manejo de precisi\u00f3n num\u00e9rica con [&hellip;]<\/p>\n","protected":false,"raw":""},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_locale":"es_ES","_original_post":"https:\/\/matforge.org\/?p=187","iawp_total_views":0,"footnotes":""},"categories":[3],"tags":[],"class_list":["post-602","post","type-post","status-publish","format-standard","hentry","category-issue-tracking-tickets-technical-requests","es-ES"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Pruebas unitarias para c\u00f3digo cient\u00edfico: estrategias PYTEST para proyectos de investigaci\u00f3n - matforge.org<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Pruebas unitarias para c\u00f3digo cient\u00edfico: estrategias PYTEST para proyectos de investigaci\u00f3n - matforge.org\" \/>\n<meta property=\"og:description\" content=\"Reading Time:  11 minutesLas pruebas unitarias no son negociables para software cient\u00edfico de confianza. A diferencia de las aplicaciones comerciales, el c\u00f3digo de investigaci\u00f3n a menudo carece de pruebas formales, lo que conduce a resultados irreproducibles y un esfuerzo desperdiciado. Esta gu\u00eda cubre las estrategias de PyTest espec\u00edficamente para proyectos cient\u00edficos de Python: manejo de precisi\u00f3n num\u00e9rica con [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/\" \/>\n<meta property=\"og:site_name\" content=\"matforge.org\" \/>\n<meta property=\"article:published_time\" content=\"2026-07-22T08:16:54+00:00\" \/>\n<meta name=\"author\" content=\"Priya Nair\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Escrito por\" \/>\n\t<meta name=\"twitter:data1\" content=\"Priya Nair\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tiempo de lectura\" \/>\n\t<meta name=\"twitter:data2\" content=\"18 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/matforge.org\\\/es\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/es\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/\"},\"author\":{\"name\":\"Priya Nair\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/2effd7bc155a5e6357f31dac970c5795\"},\"headline\":\"Pruebas unitarias para c\u00f3digo cient\u00edfico: estrategias PYTEST para proyectos de investigaci\u00f3n\",\"datePublished\":\"2026-07-22T08:16:54+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/es\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/\"},\"wordCount\":2802,\"commentCount\":0,\"articleSection\":[\"Seguimiento de problemas, tickets &amp; Solicitudes T\u00e9cnicas\"],\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/matforge.org\\\/es\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/matforge.org\\\/es\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/\",\"url\":\"https:\\\/\\\/matforge.org\\\/es\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/\",\"name\":\"Pruebas unitarias para c\u00f3digo cient\u00edfico: estrategias PYTEST para proyectos de investigaci\u00f3n - matforge.org\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/#website\"},\"datePublished\":\"2026-07-22T08:16:54+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/2effd7bc155a5e6357f31dac970c5795\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/es\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/matforge.org\\\/es\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/matforge.org\\\/es\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/matforge.org\\\/es\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Pruebas unitarias para c\u00f3digo cient\u00edfico: estrategias PYTEST para proyectos de investigaci\u00f3n\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#website\",\"url\":\"https:\\\/\\\/matforge.org\\\/\",\"name\":\"matforge.org\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/matforge.org\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/2effd7bc155a5e6357f31dac970c5795\",\"name\":\"Priya Nair\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g\",\"caption\":\"Priya Nair\"},\"sameAs\":[\"http:\\\/\\\/matforge.org\"],\"url\":\"https:\\\/\\\/matforge.org\\\/author\\\/priya-nair\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Pruebas unitarias para c\u00f3digo cient\u00edfico: estrategias PYTEST para proyectos de investigaci\u00f3n - matforge.org","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/","og_locale":"es_ES","og_type":"article","og_title":"Pruebas unitarias para c\u00f3digo cient\u00edfico: estrategias PYTEST para proyectos de investigaci\u00f3n - matforge.org","og_description":"Reading Time:  11 minutesLas pruebas unitarias no son negociables para software cient\u00edfico de confianza. A diferencia de las aplicaciones comerciales, el c\u00f3digo de investigaci\u00f3n a menudo carece de pruebas formales, lo que conduce a resultados irreproducibles y un esfuerzo desperdiciado. Esta gu\u00eda cubre las estrategias de PyTest espec\u00edficamente para proyectos cient\u00edficos de Python: manejo de precisi\u00f3n num\u00e9rica con [&hellip;]","og_url":"https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/","og_site_name":"matforge.org","article_published_time":"2026-07-22T08:16:54+00:00","author":"Priya Nair","twitter_card":"summary_large_image","twitter_misc":{"Escrito por":"Priya Nair","Tiempo de lectura":"18 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/#article","isPartOf":{"@id":"https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/"},"author":{"name":"Priya Nair","@id":"https:\/\/matforge.org\/#\/schema\/person\/2effd7bc155a5e6357f31dac970c5795"},"headline":"Pruebas unitarias para c\u00f3digo cient\u00edfico: estrategias PYTEST para proyectos de investigaci\u00f3n","datePublished":"2026-07-22T08:16:54+00:00","mainEntityOfPage":{"@id":"https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/"},"wordCount":2802,"commentCount":0,"articleSection":["Seguimiento de problemas, tickets &amp; Solicitudes T\u00e9cnicas"],"inLanguage":"es","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/","url":"https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/","name":"Pruebas unitarias para c\u00f3digo cient\u00edfico: estrategias PYTEST para proyectos de investigaci\u00f3n - matforge.org","isPartOf":{"@id":"https:\/\/matforge.org\/#website"},"datePublished":"2026-07-22T08:16:54+00:00","author":{"@id":"https:\/\/matforge.org\/#\/schema\/person\/2effd7bc155a5e6357f31dac970c5795"},"breadcrumb":{"@id":"https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/matforge.org\/es\/unit-testing-scientific-code-pytest-strategies-research-projects\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/matforge.org\/es\/"},{"@type":"ListItem","position":2,"name":"Pruebas unitarias para c\u00f3digo cient\u00edfico: estrategias PYTEST para proyectos de investigaci\u00f3n"}]},{"@type":"WebSite","@id":"https:\/\/matforge.org\/#website","url":"https:\/\/matforge.org\/","name":"matforge.org","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/matforge.org\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Person","@id":"https:\/\/matforge.org\/#\/schema\/person\/2effd7bc155a5e6357f31dac970c5795","name":"Priya Nair","image":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/secure.gravatar.com\/avatar\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g","caption":"Priya Nair"},"sameAs":["http:\/\/matforge.org"],"url":"https:\/\/matforge.org\/author\/priya-nair\/"}]}},"_links":{"self":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/602","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/comments?post=602"}],"version-history":[{"count":1,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/602\/revisions"}],"predecessor-version":[{"id":687,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/602\/revisions\/687"}],"wp:attachment":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/media?parent=602"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/categories?post=602"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/tags?post=602"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}