{"id":1258,"date":"2026-08-21T14:31:42","date_gmt":"2026-08-21T14:31:42","guid":{"rendered":"https:\/\/matforge.org\/?p=1258","raw":"https:\/\/matforge.org\/?p=1258"},"modified":"2026-08-21T14:31:42","modified_gmt":"2026-08-21T14:31:42","slug":"python-testing-patterns-for-scientific-code-beyond-pytest-basics","status":"publish","type":"post","link":"https:\/\/matforge.org\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/","title":{"rendered":"Mod\u00e8les de test Python pour le code scientifique : Beyond Pytest Basics","raw":"Mod\u00e8les de test Python pour le code scientifique : Beyond Pytest Basics"},"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\"> 9<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span><h2>Points \u00e0 retenir cl\u00e9s<\/h2>\n<ul>\n<li>Les bases de Pytest couvrent les tests unitaires. Les luminaires, la param\u00e9trisation et <code>pytest.approx<\/code> sont votre point de d\u00e9part, pas la ligne d&rsquo;arriv\u00e9e.<\/li>\n<li>Les tests bas\u00e9s sur la propri\u00e9t\u00e9 avec une hypoth\u00e8se attrapent des cas de bord que les tests manuels manquent souvent. Il est utilis\u00e9 par les principaux projets scientifiques de Python tels que NumPy, Jax et Pytorch.<\/li>\n<li>Les tests de convergence num\u00e9rique n\u00e9cessitent des strat\u00e9gies de tol\u00e9rance sp\u00e9cifiques au solveur. Les assertions standard ne capturent pas le comportement de convergence.<\/li>\n<li>Les tests d&rsquo;int\u00e9gration pour les flux de travail de simulation n\u00e9cessitent des mod\u00e8les de moquerie qui isolent les d\u00e9pendances externes tout en pr\u00e9servant la logique num\u00e9rique.<\/li>\n<li>Les tests de reproductibilit\u00e9 vont au-del\u00e0 des tests unitaires. Vous avez besoin d&rsquo;une gestion d\u00e9terministe des semences et d&rsquo;une v\u00e9rification \u00e0 virgule flottante multiplateforme.<\/li>\n<\/ul>\n<h2>Ce qu&rsquo;il faut savoir d&rsquo;abord<\/h2>\n<p>Si vous connaissez d\u00e9j\u00e0 les bases des tests unitaires de code scientifique, vous comprenez probablement comment \u00e9crire une fonction <code>test_<\/code>, quand utiliser les appareils et comment <code>pytest.approx<\/code> g\u00e8re les comparaisons \u00e0 virgule flottante. C&rsquo;est la ligne de base.<\/p>\n<p>Le code scientifique pr\u00e9sente des d\u00e9fis auxquels les tests unitaires standard ne sont pas enti\u00e8rement r\u00e9solus. Les algorithmes num\u00e9riques convergent ou ne parviennent pas \u00e0 converger. Les simulations d\u00e9pendent de solveurs externes et de syst\u00e8mes d&rsquo;E\/S. L&rsquo;arithm\u00e9tique \u00e0 virgule flottante peut se comporter diff\u00e9remment sur les plates-formes.<\/p>\n<p>Les cas de p\u00e9riph\u00e9rie qui ne se produisent jamais dans les donn\u00e9es normales, telles que les diviseurs z\u00e9ro, les matrices singuli\u00e8res et les rapports d&rsquo;aspect extr\u00eames, sont souvent l\u00e0 o\u00f9 les bogues les plus graves se cachent.<\/p>\n<p>Cet article couvre les mod\u00e8les de test du code Python scientifique au-del\u00e0 des bases.<\/p>\n<h2>La pyramide de test pour le code scientifique<\/h2>\n<p>Chaque strat\u00e9gie de test a sa place sur la pyramide. Pour le code scientifique, la structure ressemble \u00e0 ceci :<\/p>\n<pre><code>    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n    \u2502    System \/ Simulation Tests \u2502  \u2190 Integration-level workflows\n    \u2502    (3-5 tests)               \u2502\n    \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n    \u2502  Integration \/ Workflow      \u2502  \u2190 Multi-step simulations\n    \u2502  Tests (10-20 tests)         \u2502  \u2190 Data pipelines\n    \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n    \u2502  Property-Based Tests        \u2502  \u2190 Hypothesis with numerical properties\n    \u2502  (50-100 tests)              \u2502  \u2190 Convergence testing\n    \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n    \u2502  Unit Tests                  \u2502  \u2190 Numerical assertions\n    \u2502  (50-200 tests)              \u2502  \u2190 Fixtures for solvers and meshes\n    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/code><\/pre>\n<p>Les tests unitaires forment la base. Au-dessus d&rsquo;eux se trouvent des tests bas\u00e9s sur des propri\u00e9t\u00e9s, des tests d&rsquo;int\u00e9gration et des tests de simulation au niveau du syst\u00e8me. Ces couches sup\u00e9rieures rendent les tests utiles pour de v\u00e9ritables flux de travail scientifiques.<\/p>\n<h2>Tests bas\u00e9s sur la propri\u00e9t\u00e9 avec hypoth\u00e8se<\/h2>\n<h3>Pourquoi les tests bas\u00e9s sur la propri\u00e9t\u00e9 sont importants pour le code scientifique<\/h3>\n<p>L&rsquo;hypoth\u00e8se n&rsquo;est pas seulement un outil p\u00e9dagogique. Il est utilis\u00e9 par les principales biblioth\u00e8ques scientifiques de Python pour trouver des bogues dans les routines num\u00e9riques de base.<\/p>\n<p>Les tests bas\u00e9s sur la propri\u00e9t\u00e9 modifient le mod\u00e8le mental du test. Au lieu d&rsquo;\u00e9crire des tests avec une entr\u00e9e sp\u00e9cifique et une sortie attendue, vous \u00e9crivez des tests qui v\u00e9rifient les propri\u00e9t\u00e9s math\u00e9matiques. L&rsquo;hypoth\u00e8se g\u00e9n\u00e8re ensuite de nombreuses entr\u00e9es automatiquement, y compris les cas de bord que vous ne pensez peut-\u00eatre pas tester manuellement.<\/p>\n<h3>Conversion d&rsquo;un test manuscrit en propri\u00e9t\u00e9<\/h3>\n<p>Consid\u00e9rez ce test pour un encapsuleur num\u00e9rique de solveur&nbsp;:<\/p>\n<pre><code class=\"language-python\"># Before: handwritten test with single case\ndef test_solver_preserves_mass():\n    \"\"\"Check mass conservation for one mesh size.\"\"\"\n    result = solver_solve(mass_mesh, dt=0.01)\n    mass_before = sum(result[0])\n    mass_after = sum(result[-1])\n    assert mass_after \/ mass_before == pytest.approx(1.0, rel=1e-3)\n<\/code><\/pre>\n<p>Ce test v\u00e9rifie une taille de maillage et un pas de temps. Il peut passer m\u00eame si le solveur \u00e9choue pour des maillages plus grands ou des valeurs de pas de temps diff\u00e9rentes.<\/p>\n<p>Voici une version d&rsquo;hypoth\u00e8se :<\/p>\n<pre><code class=\"language-python\">from hypothesis import given, strategies as st\nfrom hypothesis import settings, health_check, suppress\n\n@given(\n    st.integers(min_value=5, max_value=200),\n    st.floats(min_value=1e-6, max_value=1.0)\n)\n@settings(max_examples=100, deadline=None)\n@health_check(\"init_consistency_suppressor\")\ndef test_solver_mass_conservation(n_cells, dt):\n    \"\"\"Mass should be preserved across valid mesh sizes and timesteps.\"\"\"\n    mesh = create_mesh(n_cells)\n    result = solver_solve(mesh, dt=dt)\n    \n    mass_before = float(sum(result[0]))\n    mass_after = float(sum(result[-1]))\n    \n    ratio = mass_after \/ mass_before\n    assert abs(ratio - 1.0) &lt; 1e-2\n<\/code><\/pre>\n<p>Cela attrape les probl\u00e8mes qu&rsquo;un test manuscrit peut manquer&nbsp;:<\/p>\n<ul>\n<li>Cas d&rsquo;angle dans la g\u00e9n\u00e9ration de maillage, y compris les domaines fins et les rapports d&rsquo;aspect extr\u00eames.<\/li>\n<li>Sensibilit\u00e9 au pas de temps, en particulier lorsque les valeurs volumineuses <code>dt<\/code> d\u00e9stabilisent les sch\u00e9mas explicites.<\/li>\n<li>Cas d&rsquo;ar\u00eate \u00e0 virgule flottante, y compris les limites arrondies et les plages num\u00e9riques inhabituelles.<\/li>\n<\/ul>\n<h3>La liste de contr\u00f4le des propri\u00e9t\u00e9s pour les fonctions scientifiques<\/h3>\n<p>Avant d&rsquo;\u00e9crire un test de propri\u00e9t\u00e9, identifiez les propri\u00e9t\u00e9s que la fonction doit satisfaire.<\/p>\n<table class=\"custom-table\">\n<tbody>\n<tr>\n<th>Type de propri\u00e9t\u00e9<\/th>\n<th>Exemple<\/th>\n<th>Pourquoi c&rsquo;est important<\/th>\n<\/tr>\n<tr>\n<td>Pr\u00e9servation<\/td>\n<td>La masse, l&rsquo;\u00e9nergie ou la quantit\u00e9 de mouvement est conserv\u00e9e<\/td>\n<td>Emp\u00eache la d\u00e9rive num\u00e9rique<\/td>\n<\/tr>\n<tr>\n<td>Sym\u00e9trie<\/td>\n<td><code>f(a, b) == f(b, a)<\/code> pour les op\u00e9rateurs sym\u00e9triques<\/td>\n<td>R\u00e9cup\u00e8re les bogues de mise en \u0153uvre asym\u00e9trique<\/td>\n<\/tr>\n<tr>\n<td>\u00c9caillage<\/td>\n<td><code>f(k*a, k*b) == k^n * f(a, b)<\/code> pour les fonctions homog\u00e8nes<\/td>\n<td>Tests coh\u00e9rence dimensionnelle<\/td>\n<\/tr>\n<tr>\n<td>monotonie<\/td>\n<td>La temp\u00e9rature diminue avec la distance dans un simple probl\u00e8me de chaleur<\/td>\n<td>V\u00e9rifie l&rsquo;exactitude physique<\/td>\n<\/tr>\n<tr>\n<td>Bornes<\/td>\n<td>Les valeurs restent dans une plage valide<\/td>\n<td>Emp\u00eache les r\u00e9sultats non physiques<\/td>\n<\/tr>\n<tr>\n<td>Comportement nul<\/td>\n<td><code>f(0)<\/code> renvoie la valeur de cas de bord attendue<\/td>\n<td>Les gardes contre la division par des cas nuls et singuliers<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Test de convergence num\u00e9rique<\/h2>\n<h3>Pourquoi les tests de convergence sont diff\u00e9rents<\/h3>\n<p>Les tests unitaires v\u00e9rifient si le code produit une r\u00e9ponse sp\u00e9cifique. Les tests de convergence v\u00e9rifient si le code approche de la bonne r\u00e9ponse \u00e0 mesure que les param\u00e8tres sont affin\u00e9s.<\/p>\n<p>Ceci est essentiel pour les solveurs it\u00e9ratifs, les sch\u00e9mas de pas de temps et les \u00e9tudes de raffinement de maillage. Les assertions standard telles que <code>assert result == expected<\/code> ne capturent pas le comportement de convergence. Vous devez tester les mod\u00e8les de convergence.<\/p>\n<h3>Tester la convergence du solveur<\/h3>\n<p>Diff\u00e9rentes suites de solveurs utilisent des crit\u00e8res de convergence diff\u00e9rents. Le point cl\u00e9 est que les crit\u00e8res de convergence sont sp\u00e9cifiques au solveur et que la tol\u00e9rance par d\u00e9faut peut ne pas convenir \u00e0 chaque probl\u00e8me.<\/p>\n<pre><code class=\"language-python\">import numpy as np\n\ndef test_solver_convergence():\n    \"\"\"Test that the solver converges with mesh refinement.\"\"\"\n    residuals = []\n    for n in [16, 32, 64, 128, 256]:\n        mesh = create_mesh(n)\n        result = solve_with_convergence(\n            mesh,\n            criterion=\"default\",\n            tolerance=1e-8\n        )\n        residuals.append(result.residual)\n    \n    residuals = np.array(residuals)\n    \n    # Test that residuals decrease monotonically with tolerance\n    for i in range(1, len(residuals)):\n        if residuals[i] &gt; residuals[i-1] * 1.01:\n            raise AssertionError(\n                f\"Residual increased at refinement level {i}: \"\n                f\"{residuals[i-1]:.2e} \u2192 {residuals[i]:.2e}\"\n            )\n    \n    # Test convergence rate\n    rate = np.log(residuals[0] \/ residuals[-1]) \/ np.log(16)\n    assert rate &gt;= 1.8, f\"Convergence rate too slow: {rate:.2f}\"\n<\/code><\/pre>\n<h3>strat\u00e9gies de tol\u00e9rance<\/h3>\n<p>Les tests doivent couvrir les crit\u00e8res de tol\u00e9rance les plus couramment utilis\u00e9s par la pile de solveurs.<\/p>\n<pre><code class=\"language-python\">@pytest.mark.parametrize(\"criterion\", [\n    \"default\",\n    \"unscaled\",\n    \"preconditioned\",\n    \"natural\",\n])\ndef test_convergence_criteria(criterion):\n    \"\"\"Verify each criterion produces reasonable residuals.\"\"\"\n    mesh = create_mesh(64)\n    result = solve_with_convergence(mesh, criterion=criterion)\n    \n    assert result.status in (\n        \"convergence\",\n        \"absolute_tolerance_convergence\",\n        \"relative_tolerance_convergence\",\n        \"happy_breakdown\"\n    ) or result.residual &lt; 1e-2\n<\/code><\/pre>\n<h3>Tester des cas de divergence<\/h3>\n<p>Vous devez \u00e9galement tester que le code g\u00e8re les divergences avec \u00e9l\u00e9gance.<\/p>\n<pre><code class=\"language-python\">def test_divergence_handling():\n    \"\"\"Code should not crash on divergent cases.\"\"\"\n    mesh = create_mesh(64)\n    result = solve_with_convergence(\n        mesh,\n        criterion=\"default\",\n        divergence_tolerance=100\n    )\n    \n    assert hasattr(result, \"status\")\n    assert hasattr(result, \"residual\")\n<\/code><\/pre>\n<h2>Test d&rsquo;int\u00e9gration pour les flux de travail de simulation<\/h2>\n<h3>Pourquoi les tests d&rsquo;int\u00e9gration sont importants<\/h3>\n<p>Les tests unitaires v\u00e9rifient les fonctions individuelles. Les tests d&rsquo;int\u00e9gration v\u00e9rifient que les fonctions fonctionnent correctement ensemble.<\/p>\n<p>Pour le code scientifique, cela signifie tester des simulations en plusieurs \u00e9tapes, des pipelines de donn\u00e9es, des mod\u00e8les de couplage de solveur et des flux de travail d&rsquo;E\/S.<\/p>\n<h3>Mod\u00e8le 1 : moquerie des d\u00e9pendances externes dans le code scientifique<\/h3>\n<p>Lorsque vous testez des fonctions qui appellent des API, des bases de donn\u00e9es ou des syst\u00e8mes de fichiers externes, utilisez la moquerie pour isoler la logique num\u00e9rique.<\/p>\n<pre><code class=\"language-python\">from unittest.mock import patch, MagicMock\n\ndef test_simulation_io():\n    \"\"\"Test simulation I\/O without actually writing to disk.\"\"\"\n    \n    with patch(\"h5py.File\") as mock_file:\n        mock_dataset = MagicMock()\n        mock_file.return_value = MagicMock()\n        mock_file.return_value.__enter__ = MagicMock(return_value=mock_file)\n        mock_file.return_value.__exit__ = MagicMock(return_value=None)\n        \n        data = prepare_simulation_output(mesh_size=(100, 100, 100))\n        \n        assert \"simulation_data\" in mock_file.return_value.keys()\n        assert data.shape == (100, 100, 100)\n<\/code><\/pre>\n<p>Cela isole la logique de pr\u00e9paration des donn\u00e9es de l&rsquo;\u00e9criture de fichier HDF5. Le test v\u00e9rifie que les formats de code et de package sont correctement d\u00e9finis sans attendre les E\/S.<\/p>\n<h3>Mod\u00e8le 2 : Appareils param\u00e9tr\u00e9s pour les balayages de simulation<\/h3>\n<p>Au lieu de tester manuellement quelques tailles de maillage, utilisez des appareils param\u00e9tr\u00e9s pour des balayages syst\u00e9matiques.<\/p>\n<pre><code class=\"language-python\">@pytest.fixture(params=[10, 20, 40, 80, 160])\ndef mesh_sizes(request):\n    \"\"\"Systematic mesh refinement series.\"\"\"\n    return request.param\n\n@pytest.fixture(params=[\n    (1e-3, \"fine\"),\n    (1e-2, \"medium\"), \n    (1e-1, \"coarse\")\n])\ndef timesteps(request):\n    \"\"\"Test different timestep regimes.\"\"\"\n    dt, label = request.param\n    return dt, label\n\ndef test_convergence_with_sweeps(mesh_sizes, timesteps):\n    \"\"\"Test convergence across mesh and timestep refinement.\"\"\"\n    dt, label = timesteps\n    \n    result = solve_convergence_study(\n        mesh_sizes,\n        dt=dt,\n        quantity=\"mass_conservation\"\n    )\n    \n    assert len(result.residuals) == len(result.mesh_sizes)\n    assert all(r &gt; 0 for r in result.residuals)\n<\/code><\/pre>\n<h3>Mod\u00e8le&nbsp;3&nbsp;: tester des flux de travail en plusieurs \u00e9tapes<\/h3>\n<p>Les simulations scientifiques ont souvent des pipelines en plusieurs \u00e9tapes : g\u00e9n\u00e9ration de maillage, r\u00e9solution, post-traitement et production d&rsquo;\u00e9criture.<\/p>\n<pre><code class=\"language-python\">def test_full_workflow():\n    \"\"\"Test the complete simulation pipeline.\"\"\"\n    # Step 1: Generate mesh\n    mesh = generate_mesh(geometry=\"block\", resolution=40)\n    \n    # Step 2: Solve\n    solution = solve(mesh, solver=\"scipy\", tolerance=1e-8)\n    \n    # Step 3: Post-process\n    metrics = compute_metrics(solution, quantities=[\"mass\", \"energy\"])\n    \n    # Step 4: Verify outputs\n    assert metrics[\"mass\"] &gt; 0\n    assert metrics[\"energy\"] &lt; 1e6\n    \n    # Step 5: Write output\n    output_path = write_output(solution, path=\"\/tmp\/test_output.h5\")\n    \n    # Verify output is readable\n    readback = h5py.File(output_path, \"r\")\n    assert \"solution\" in readback.keys()\n<\/code><\/pre>\n<h2>Moquerie avanc\u00e9e pour les biblioth\u00e8ques scientifiques<\/h2>\n<h3>Le d\u00e9fi<\/h3>\n<p>Le test de code scientifique peut \u00eatre lent, car les fonctions NumPy, Scipy et Fipy peuvent avoir des types de retour complexes et des chemins d&rsquo;ex\u00e9cution co\u00fbteux. Les moquer vous permet de tester rapidement la logique de l&rsquo;encapsuleur.<\/p>\n<pre><code class=\"language-python\">import numpy as np\nfrom unittest.mock import patch\n\ndef test_solver_wrapper():\n    \"\"\"Test that your wrapper calls scipy.sparse.linalg correctly.\"\"\"\n    \n    with patch(\"scipy.sparse.linalg.spsolve\") as mock_solve:\n        mock_solve.return_value = np.zeros(100)\n        \n        result = your_solver_wrapper(mesh_size=100)\n        \n        mock_solve.assert_called_once()\n        call_args = mock_solve.call_args\n        assert isinstance(call_args[0], np.ndarray)\n        assert isinstance(call_args[1], np.ndarray)\n<\/code><\/pre>\n<p>Cela v\u00e9rifie la logique d&rsquo;encapsulation sans attendre l&rsquo;ex\u00e9cution du solveur r\u00e9el. Le test peut se terminer en millisecondes au lieu de secondes.<\/p>\n<h3>Test des allers-retours pour les donn\u00e9es scientifiques<\/h3>\n<p>Le test que les donn\u00e9es scientifiques survivent \u00e0 un cycle lecture-\u00e9criture-lecture est un mod\u00e8le puissant.<\/p>\n<pre><code class=\"language-python\">from hypothesis import given, strategies as st\n\n@given(st.integers(min_value=10, max_value=200))\ndef test_hdf5_roundtrip(n_cells):\n    \"\"\"Writing and reading HDF5 data should preserve values.\"\"\"\n    mesh = create_mesh(n_cells)\n    solution = solve(mesh, dt=0.01)\n    \n    path = write_to_hdf5(solution, path=\"\/tmp\/test_roundtrip.h5\")\n    \n    readback = read_from_hdf5(path)\n    \n    assert np.allclose(readback, solution, rtol=1e-6, atol=1e-8)\n<\/code><\/pre>\n<p>Cela permet de d\u00e9tecter les bogues de s\u00e9rialisation des donn\u00e9es, les incompatibilit\u00e9s de type DTYPE et les probl\u00e8mes de segmentation HDF5 qui pourraient silencieusement des r\u00e9sultats corrompus.<\/p>\n<h2>Reproductibilit\u00e9 des tests<\/h2>\n<h3>Gestion des semences d\u00e9terministes<\/h3>\n<p>Les op\u00e9rations \u00e0 virgule flottante peuvent \u00eatre d\u00e9terministes lorsque la graine est fixe, mais la reproductibilit\u00e9 n\u00e9cessite \u00e9galement des versions de biblioth\u00e8que coh\u00e9rentes, des indicateurs de compilation et des mod\u00e8les de d\u00e9composition MPI.<\/p>\n<pre><code class=\"language-python\">def test_reproducible_seed():\n    \"\"\"Fixed seed should produce identical results.\"\"\"\n    np.random.seed(42)\n    \n    mesh1 = generate_mesh(seed=42)\n    solution1 = solve(mesh1)\n    \n    np.random.seed(42)\n    mesh2 = generate_mesh(seed=42)\n    solution2 = solve(mesh2)\n    \n    assert np.allclose(solution1, solution2)\n<\/code><\/pre>\n<h3>Essais \u00e0 virgule flottante multiplateforme<\/h3>\n<p>Diff\u00e9rentes plates-formes peuvent produire des r\u00e9sultats \u00e0 virgule flottante l\u00e9g\u00e8rement diff\u00e9rents en raison des diff\u00e9rences de mat\u00e9riel et de biblioth\u00e8que.<\/p>\n<pre><code class=\"language-python\">def test_floating_point_stability():\n    \"\"\"Results should be consistent across platforms within tolerance.\"\"\"\n    mesh = create_mesh(64)\n    solution = solve(mesh)\n    \n    # All values should be finite\n    assert np.all(np.isfinite(solution))\n    \n    # Values should not be suspiciously small\n    assert np.all(np.abs(solution) &gt; np.finfo(np.float64).tiny * 1e-3)\n<\/code><\/pre>\n<h2>Ce que nous recommandons : la strat\u00e9gie de test<\/h2>\n<p>Pour un projet Python scientifique de production, utilisez une strat\u00e9gie de test en couches.<\/p>\n<h3>Doit avoir<\/h3>\n<ol>\n<li>Tests unitaires avec <code>pytest.approx<\/code> pour les assertions num\u00e9riques.<\/li>\n<li>Appareils param\u00e9tr\u00e9s pour des tests syst\u00e9matiques \u00e0 travers les param\u00e8tres.<\/li>\n<li>Tests de convergence qui v\u00e9rifient le comportement du solveur \u00e0 travers le raffinement.<\/li>\n<\/ol>\n<h3>Aurait d\u00fb<\/h3>\n<ol start=\"4\">\n<li>Hypoth\u00e8se Tests bas\u00e9s sur la propri\u00e9t\u00e9 pour les cas de bord et la robustesse num\u00e9rique.<\/li>\n<li>Tests d&rsquo;int\u00e9gration pour les flux de travail de simulation en plusieurs \u00e9tapes.<\/li>\n<li>Strat\u00e9gies de moquerie pour les d\u00e9pendances externes et les interfaces de solveur.<\/li>\n<\/ol>\n<h3>Agr\u00e9able d&rsquo;avoir<\/h3>\n<ol start=\"7\">\n<li>Tests de reproductibilit\u00e9 multi-plateformes.<\/li>\n<li>Data roundtrip teste qui \u00e9crit, lise et v\u00e9rifie les donn\u00e9es scientifiques.<\/li>\n<li>Benchmarks de performance avec <code>pytest-benchmark<\/code>.<\/li>\n<\/ol>\n<h2>erreurs courantes<\/h2>\n<h3>Erreur&nbsp;1&nbsp;: utilisation incorrecte des assertions d\u00e9pendantes de la tol\u00e9rance<\/h3>\n<p>N&rsquo;\u00e9crivez pas de tests num\u00e9riques comme <code>assert result == expected<\/code>. Utilisez <code>pytest.approx<\/code> ou les assertions sensibles \u00e0 la tol\u00e9rance pour le code num\u00e9rique. La tol\u00e9rance d\u00e9pend de l&rsquo;\u00e9chelle du probl\u00e8me, donc une tol\u00e9rance fixe peut \u00e9chouer pour des probl\u00e8mes plus importants.<\/p>\n<h3>Erreur&nbsp;2&nbsp;: ignorer la convergence<\/h3>\n<p>Si le code utilise des solveurs it\u00e9ratifs, n&rsquo;ignorez pas les tests de convergence. Un solveur qui fonctionne sur un maillage peut diverger sur un autre. Testez le comportement de convergence, pas seulement le r\u00e9sultat final.<\/p>\n<h3>Erreur&nbsp;3&nbsp;: Tester uniquement le chemin heureux<\/h3>\n<p>Les tests bas\u00e9s sur des propri\u00e9t\u00e9s vous obligent \u00e0 tester des cas de bord que les tests manuels manquent souvent. Ajoutez au moins un test d&rsquo;hypoth\u00e8se pour chaque fonction num\u00e9rique majeure. Il peut r\u00e9v\u00e9ler des bogues que les tests normaux bas\u00e9s sur des exemples n&rsquo;attrapent jamais.<\/p>\n<h2>R\u00e9sum\u00e9<\/h2>\n<p>Les tests unitaires sont n\u00e9cessaires, mais ils ne sont pas suffisants pour le code scientifique. Les tests bas\u00e9s sur des propri\u00e9t\u00e9s, les tests de convergence, les flux de travail d&rsquo;int\u00e9gration et les contr\u00f4les de reproductibilit\u00e9 r\u00e9pondent aux d\u00e9fis sp\u00e9cifiques que les simulations num\u00e9riques introduisent.<\/p>\n<p>L&rsquo;id\u00e9e cl\u00e9 est que les tests scientifiques ne consistent pas seulement \u00e0 v\u00e9rifier que le code produit une bonne r\u00e9ponse. Il s&rsquo;agit de v\u00e9rifier que le code se comporte correctement. Il doit converger, conserver, adapter et g\u00e9rer les cas de bordure avec \u00e9l\u00e9gance.<\/p>\n<p>Commencez par les mod\u00e8les incontournables, puis ajoutez les mod\u00e8les de devrait avoir au fur et \u00e0 mesure que la suite de tests m\u00fbrit. Utilisez les mod\u00e8les Nice-to-Have pour durcir la base de code au fil du temps.<\/p>\n<p>Les tests bas\u00e9s sur des propri\u00e9t\u00e9s avec Hypothesis sont l&rsquo;un des ajouts les plus \u00e9lev\u00e9s que vous puissiez faire, car ils attrapent les bogues que les tests manuels manquent souvent.<\/p>\n<h2>Guides connexes<\/h2>\n<ul>\n<li><a href=\"https:\/\/matforge.org\/unit-testing-scientific-code-pytest-strategies-research-projects\/\">Tests unitaires pour le code scientifique&nbsp;: strat\u00e9gies PyTest pour des projets de recherche<\/a> &#8211; la fondation dont vous avez besoin avant cet article.<\/li>\n<li><a href=\"https:\/\/matforge.org\/when-to-use-fem-fvm-fdm\/\">Quand utiliser FEM, FVM ou FDM&nbsp;: une comparaison pratique pour les d\u00e9butants<\/a> \u2014 Contexte sur les m\u00e9thodes num\u00e9riques.<\/li>\n<li><a href=\"https:\/\/matforge.org\/mesh-quality-convergence-studies-guide-2\/\">\u00c9tudes de qualit\u00e9 et de convergence du maillage&nbsp;: un guide pratique pour les simulations scientifiques<\/a> \u2014 contexte de test de convergence connexe.<\/li>\n<\/ul>\n<h2>Que faire ensuite<\/h2>\n<p>Si vous venez de commencer \u00e0 \u00e9crire des tests de code scientifique, commencez par les mod\u00e8les incontournables ci-dessus. Une fois ceux-ci en place, ajoutez des tests d&rsquo;hypoth\u00e8se pour les fonctions num\u00e9riques les plus importantes, en particulier les fonctions appel\u00e9es \u00e0 plusieurs reprises dans les simulations.<\/p>\n<p>Pour les \u00e9quipes travaillant sur des logiciels scientifiques de production, l&rsquo;investissement dans des tests bas\u00e9s sur la propri\u00e9t\u00e9 peut \u00eatre payant rapidement. Il s&rsquo;agit de la m\u00eame strat\u00e9gie utilis\u00e9e par les principales biblioth\u00e8ques scientifiques Python et l&rsquo;un des moyens les plus efficaces de trouver des bogues de cas de bord dans le code num\u00e9rique.<\/p>\n<p>Si vous avez besoin d&rsquo;aide pour concevoir une strat\u00e9gie de test pour un projet de simulation, envisagez de r\u00e9server une consultation. Un examen pratique peut associer votre structure de code aux tests unitaires, aux tests de convergence, aux tests d&rsquo;int\u00e9gration et aux contr\u00f4les de reproductibilit\u00e9.<\/p>\n","protected":false,"raw":"<h2>Points \u00e0 retenir cl\u00e9s<\/h2>\n<ul>\n<li>Les bases de Pytest couvrent les tests unitaires. Les luminaires, la param\u00e9trisation et <code>pytest.approx<\/code> sont votre point de d\u00e9part, pas la ligne d'arriv\u00e9e.<\/li>\n<li>Les tests bas\u00e9s sur la propri\u00e9t\u00e9 avec une hypoth\u00e8se attrapent des cas de bord que les tests manuels manquent souvent. Il est utilis\u00e9 par les principaux projets scientifiques de Python tels que NumPy, Jax et Pytorch.<\/li>\n<li>Les tests de convergence num\u00e9rique n\u00e9cessitent des strat\u00e9gies de tol\u00e9rance sp\u00e9cifiques au solveur. Les assertions standard ne capturent pas le comportement de convergence.<\/li>\n<li>Les tests d'int\u00e9gration pour les flux de travail de simulation n\u00e9cessitent des mod\u00e8les de moquerie qui isolent les d\u00e9pendances externes tout en pr\u00e9servant la logique num\u00e9rique.<\/li>\n<li>Les tests de reproductibilit\u00e9 vont au-del\u00e0 des tests unitaires. Vous avez besoin d'une gestion d\u00e9terministe des semences et d'une v\u00e9rification \u00e0 virgule flottante multiplateforme.<\/li>\n<\/ul>\n<h2>Ce qu'il faut savoir d'abord<\/h2>\n<p>Si vous connaissez d\u00e9j\u00e0 les bases des tests unitaires de code scientifique, vous comprenez probablement comment \u00e9crire une fonction <code>test_<\/code>, quand utiliser les appareils et comment <code>pytest.approx<\/code> g\u00e8re les comparaisons \u00e0 virgule flottante. C'est la ligne de base.<\/p>\n<p>Le code scientifique pr\u00e9sente des d\u00e9fis auxquels les tests unitaires standard ne sont pas enti\u00e8rement r\u00e9solus. Les algorithmes num\u00e9riques convergent ou ne parviennent pas \u00e0 converger. Les simulations d\u00e9pendent de solveurs externes et de syst\u00e8mes d'E\/S. L'arithm\u00e9tique \u00e0 virgule flottante peut se comporter diff\u00e9remment sur les plates-formes.<\/p>\n<p>Les cas de p\u00e9riph\u00e9rie qui ne se produisent jamais dans les donn\u00e9es normales, telles que les diviseurs z\u00e9ro, les matrices singuli\u00e8res et les rapports d'aspect extr\u00eames, sont souvent l\u00e0 o\u00f9 les bogues les plus graves se cachent.<\/p>\n<p>Cet article couvre les mod\u00e8les de test du code Python scientifique au-del\u00e0 des bases.<\/p>\n<h2>La pyramide de test pour le code scientifique<\/h2>\n<p>Chaque strat\u00e9gie de test a sa place sur la pyramide. Pour le code scientifique, la structure ressemble \u00e0 ceci :<\/p>\n<pre><code>    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n    \u2502    System \/ Simulation Tests \u2502  \u2190 Integration-level workflows\n    \u2502    (3-5 tests)               \u2502\n    \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n    \u2502  Integration \/ Workflow      \u2502  \u2190 Multi-step simulations\n    \u2502  Tests (10-20 tests)         \u2502  \u2190 Data pipelines\n    \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n    \u2502  Property-Based Tests        \u2502  \u2190 Hypothesis with numerical properties\n    \u2502  (50-100 tests)              \u2502  \u2190 Convergence testing\n    \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n    \u2502  Unit Tests                  \u2502  \u2190 Numerical assertions\n    \u2502  (50-200 tests)              \u2502  \u2190 Fixtures for solvers and meshes\n    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/code><\/pre>\n<p>Les tests unitaires forment la base. Au-dessus d'eux se trouvent des tests bas\u00e9s sur des propri\u00e9t\u00e9s, des tests d'int\u00e9gration et des tests de simulation au niveau du syst\u00e8me. Ces couches sup\u00e9rieures rendent les tests utiles pour de v\u00e9ritables flux de travail scientifiques.<\/p>\n<h2>Tests bas\u00e9s sur la propri\u00e9t\u00e9 avec hypoth\u00e8se<\/h2>\n<h3>Pourquoi les tests bas\u00e9s sur la propri\u00e9t\u00e9 sont importants pour le code scientifique<\/h3>\n<p>L'hypoth\u00e8se n'est pas seulement un outil p\u00e9dagogique. Il est utilis\u00e9 par les principales biblioth\u00e8ques scientifiques de Python pour trouver des bogues dans les routines num\u00e9riques de base.<\/p>\n<p>Les tests bas\u00e9s sur la propri\u00e9t\u00e9 modifient le mod\u00e8le mental du test. Au lieu d'\u00e9crire des tests avec une entr\u00e9e sp\u00e9cifique et une sortie attendue, vous \u00e9crivez des tests qui v\u00e9rifient les propri\u00e9t\u00e9s math\u00e9matiques. L'hypoth\u00e8se g\u00e9n\u00e8re ensuite de nombreuses entr\u00e9es automatiquement, y compris les cas de bord que vous ne pensez peut-\u00eatre pas tester manuellement.<\/p>\n<h3>Conversion d'un test manuscrit en propri\u00e9t\u00e9<\/h3>\n<p>Consid\u00e9rez ce test pour un encapsuleur num\u00e9rique de solveur&nbsp;:<\/p>\n<pre><code class=\"language-python\"># Before: handwritten test with single case\ndef test_solver_preserves_mass():\n    \"\"\"Check mass conservation for one mesh size.\"\"\"\n    result = solver_solve(mass_mesh, dt=0.01)\n    mass_before = sum(result[0])\n    mass_after = sum(result[-1])\n    assert mass_after \/ mass_before == pytest.approx(1.0, rel=1e-3)\n<\/code><\/pre>\n<p>Ce test v\u00e9rifie une taille de maillage et un pas de temps. Il peut passer m\u00eame si le solveur \u00e9choue pour des maillages plus grands ou des valeurs de pas de temps diff\u00e9rentes.<\/p>\n<p>Voici une version d'hypoth\u00e8se :<\/p>\n<pre><code class=\"language-python\">from hypothesis import given, strategies as st\nfrom hypothesis import settings, health_check, suppress\n\n@given(\n    st.integers(min_value=5, max_value=200),\n    st.floats(min_value=1e-6, max_value=1.0)\n)\n@settings(max_examples=100, deadline=None)\n@health_check(\"init_consistency_suppressor\")\ndef test_solver_mass_conservation(n_cells, dt):\n    \"\"\"Mass should be preserved across valid mesh sizes and timesteps.\"\"\"\n    mesh = create_mesh(n_cells)\n    result = solver_solve(mesh, dt=dt)\n    \n    mass_before = float(sum(result[0]))\n    mass_after = float(sum(result[-1]))\n    \n    ratio = mass_after \/ mass_before\n    assert abs(ratio - 1.0) &lt; 1e-2\n<\/code><\/pre>\n<p>Cela attrape les probl\u00e8mes qu'un test manuscrit peut manquer&nbsp;:<\/p>\n<ul>\n<li>Cas d'angle dans la g\u00e9n\u00e9ration de maillage, y compris les domaines fins et les rapports d'aspect extr\u00eames.<\/li>\n<li>Sensibilit\u00e9 au pas de temps, en particulier lorsque les valeurs volumineuses <code>dt<\/code> d\u00e9stabilisent les sch\u00e9mas explicites.<\/li>\n<li>Cas d'ar\u00eate \u00e0 virgule flottante, y compris les limites arrondies et les plages num\u00e9riques inhabituelles.<\/li>\n<\/ul>\n<h3>La liste de contr\u00f4le des propri\u00e9t\u00e9s pour les fonctions scientifiques<\/h3>\n<p>Avant d'\u00e9crire un test de propri\u00e9t\u00e9, identifiez les propri\u00e9t\u00e9s que la fonction doit satisfaire.<\/p>\n<table class=\"custom-table\">\n<tbody><tr>\n<th>Type de propri\u00e9t\u00e9<\/th>\n<th>Exemple<\/th>\n<th>Pourquoi c'est important<\/th>\n<\/tr>\n<tr>\n<td>Pr\u00e9servation<\/td>\n<td>La masse, l'\u00e9nergie ou la quantit\u00e9 de mouvement est conserv\u00e9e<\/td>\n<td>Emp\u00eache la d\u00e9rive num\u00e9rique<\/td>\n<\/tr>\n<tr>\n<td>Sym\u00e9trie<\/td>\n<td><code>f(a, b) == f(b, a)<\/code> pour les op\u00e9rateurs sym\u00e9triques<\/td>\n<td>R\u00e9cup\u00e8re les bogues de mise en \u0153uvre asym\u00e9trique<\/td>\n<\/tr>\n<tr>\n<td>\u00c9caillage<\/td>\n<td><code>f(k*a, k*b) == k^n * f(a, b)<\/code> pour les fonctions homog\u00e8nes<\/td>\n<td>Tests coh\u00e9rence dimensionnelle<\/td>\n<\/tr>\n<tr>\n<td>monotonie<\/td>\n<td>La temp\u00e9rature diminue avec la distance dans un simple probl\u00e8me de chaleur<\/td>\n<td>V\u00e9rifie l'exactitude physique<\/td>\n<\/tr>\n<tr>\n<td>Bornes<\/td>\n<td>Les valeurs restent dans une plage valide<\/td>\n<td>Emp\u00eache les r\u00e9sultats non physiques<\/td>\n<\/tr>\n<tr>\n<td>Comportement nul<\/td>\n<td><code>f(0)<\/code> renvoie la valeur de cas de bord attendue<\/td>\n<td>Les gardes contre la division par des cas nuls et singuliers<\/td>\n<\/tr>\n<\/tbody><\/table>\n<h2>Test de convergence num\u00e9rique<\/h2>\n<h3>Pourquoi les tests de convergence sont diff\u00e9rents<\/h3>\n<p>Les tests unitaires v\u00e9rifient si le code produit une r\u00e9ponse sp\u00e9cifique. Les tests de convergence v\u00e9rifient si le code approche de la bonne r\u00e9ponse \u00e0 mesure que les param\u00e8tres sont affin\u00e9s.<\/p>\n<p>Ceci est essentiel pour les solveurs it\u00e9ratifs, les sch\u00e9mas de pas de temps et les \u00e9tudes de raffinement de maillage. Les assertions standard telles que <code>assert result == expected<\/code> ne capturent pas le comportement de convergence. Vous devez tester les mod\u00e8les de convergence.<\/p>\n<h3>Tester la convergence du solveur<\/h3>\n<p>Diff\u00e9rentes suites de solveurs utilisent des crit\u00e8res de convergence diff\u00e9rents. Le point cl\u00e9 est que les crit\u00e8res de convergence sont sp\u00e9cifiques au solveur et que la tol\u00e9rance par d\u00e9faut peut ne pas convenir \u00e0 chaque probl\u00e8me.<\/p>\n<pre><code class=\"language-python\">import numpy as np\n\ndef test_solver_convergence():\n    \"\"\"Test that the solver converges with mesh refinement.\"\"\"\n    residuals = []\n    for n in [16, 32, 64, 128, 256]:\n        mesh = create_mesh(n)\n        result = solve_with_convergence(\n            mesh,\n            criterion=\"default\",\n            tolerance=1e-8\n        )\n        residuals.append(result.residual)\n    \n    residuals = np.array(residuals)\n    \n    # Test that residuals decrease monotonically with tolerance\n    for i in range(1, len(residuals)):\n        if residuals[i] &gt; residuals[i-1] * 1.01:\n            raise AssertionError(\n                f\"Residual increased at refinement level {i}: \"\n                f\"{residuals[i-1]:.2e} \u2192 {residuals[i]:.2e}\"\n            )\n    \n    # Test convergence rate\n    rate = np.log(residuals[0] \/ residuals[-1]) \/ np.log(16)\n    assert rate &gt;= 1.8, f\"Convergence rate too slow: {rate:.2f}\"\n<\/code><\/pre>\n<h3>strat\u00e9gies de tol\u00e9rance<\/h3>\n<p>Les tests doivent couvrir les crit\u00e8res de tol\u00e9rance les plus couramment utilis\u00e9s par la pile de solveurs.<\/p>\n<pre><code class=\"language-python\">@pytest.mark.parametrize(\"criterion\", [\n    \"default\",\n    \"unscaled\",\n    \"preconditioned\",\n    \"natural\",\n])\ndef test_convergence_criteria(criterion):\n    \"\"\"Verify each criterion produces reasonable residuals.\"\"\"\n    mesh = create_mesh(64)\n    result = solve_with_convergence(mesh, criterion=criterion)\n    \n    assert result.status in (\n        \"convergence\",\n        \"absolute_tolerance_convergence\",\n        \"relative_tolerance_convergence\",\n        \"happy_breakdown\"\n    ) or result.residual &lt; 1e-2\n<\/code><\/pre>\n<h3>Tester des cas de divergence<\/h3>\n<p>Vous devez \u00e9galement tester que le code g\u00e8re les divergences avec \u00e9l\u00e9gance.<\/p>\n<pre><code class=\"language-python\">def test_divergence_handling():\n    \"\"\"Code should not crash on divergent cases.\"\"\"\n    mesh = create_mesh(64)\n    result = solve_with_convergence(\n        mesh,\n        criterion=\"default\",\n        divergence_tolerance=100\n    )\n    \n    assert hasattr(result, \"status\")\n    assert hasattr(result, \"residual\")\n<\/code><\/pre>\n<h2>Test d'int\u00e9gration pour les flux de travail de simulation<\/h2>\n<h3>Pourquoi les tests d'int\u00e9gration sont importants<\/h3>\n<p>Les tests unitaires v\u00e9rifient les fonctions individuelles. Les tests d'int\u00e9gration v\u00e9rifient que les fonctions fonctionnent correctement ensemble.<\/p>\n<p>Pour le code scientifique, cela signifie tester des simulations en plusieurs \u00e9tapes, des pipelines de donn\u00e9es, des mod\u00e8les de couplage de solveur et des flux de travail d'E\/S.<\/p>\n<h3>Mod\u00e8le 1 : moquerie des d\u00e9pendances externes dans le code scientifique<\/h3>\n<p>Lorsque vous testez des fonctions qui appellent des API, des bases de donn\u00e9es ou des syst\u00e8mes de fichiers externes, utilisez la moquerie pour isoler la logique num\u00e9rique.<\/p>\n<pre><code class=\"language-python\">from unittest.mock import patch, MagicMock\n\ndef test_simulation_io():\n    \"\"\"Test simulation I\/O without actually writing to disk.\"\"\"\n    \n    with patch(\"h5py.File\") as mock_file:\n        mock_dataset = MagicMock()\n        mock_file.return_value = MagicMock()\n        mock_file.return_value.__enter__ = MagicMock(return_value=mock_file)\n        mock_file.return_value.__exit__ = MagicMock(return_value=None)\n        \n        data = prepare_simulation_output(mesh_size=(100, 100, 100))\n        \n        assert \"simulation_data\" in mock_file.return_value.keys()\n        assert data.shape == (100, 100, 100)\n<\/code><\/pre>\n<p>Cela isole la logique de pr\u00e9paration des donn\u00e9es de l'\u00e9criture de fichier HDF5. Le test v\u00e9rifie que les formats de code et de package sont correctement d\u00e9finis sans attendre les E\/S.<\/p>\n<h3>Mod\u00e8le 2 : Appareils param\u00e9tr\u00e9s pour les balayages de simulation<\/h3>\n<p>Au lieu de tester manuellement quelques tailles de maillage, utilisez des appareils param\u00e9tr\u00e9s pour des balayages syst\u00e9matiques.<\/p>\n<pre><code class=\"language-python\">@pytest.fixture(params=[10, 20, 40, 80, 160])\ndef mesh_sizes(request):\n    \"\"\"Systematic mesh refinement series.\"\"\"\n    return request.param\n\n@pytest.fixture(params=[\n    (1e-3, \"fine\"),\n    (1e-2, \"medium\"), \n    (1e-1, \"coarse\")\n])\ndef timesteps(request):\n    \"\"\"Test different timestep regimes.\"\"\"\n    dt, label = request.param\n    return dt, label\n\ndef test_convergence_with_sweeps(mesh_sizes, timesteps):\n    \"\"\"Test convergence across mesh and timestep refinement.\"\"\"\n    dt, label = timesteps\n    \n    result = solve_convergence_study(\n        mesh_sizes,\n        dt=dt,\n        quantity=\"mass_conservation\"\n    )\n    \n    assert len(result.residuals) == len(result.mesh_sizes)\n    assert all(r &gt; 0 for r in result.residuals)\n<\/code><\/pre>\n<h3>Mod\u00e8le&nbsp;3&nbsp;: tester des flux de travail en plusieurs \u00e9tapes<\/h3>\n<p>Les simulations scientifiques ont souvent des pipelines en plusieurs \u00e9tapes : g\u00e9n\u00e9ration de maillage, r\u00e9solution, post-traitement et production d'\u00e9criture.<\/p>\n<pre><code class=\"language-python\">def test_full_workflow():\n    \"\"\"Test the complete simulation pipeline.\"\"\"\n    # Step 1: Generate mesh\n    mesh = generate_mesh(geometry=\"block\", resolution=40)\n    \n    # Step 2: Solve\n    solution = solve(mesh, solver=\"scipy\", tolerance=1e-8)\n    \n    # Step 3: Post-process\n    metrics = compute_metrics(solution, quantities=[\"mass\", \"energy\"])\n    \n    # Step 4: Verify outputs\n    assert metrics[\"mass\"] &gt; 0\n    assert metrics[\"energy\"] &lt; 1e6\n    \n    # Step 5: Write output\n    output_path = write_output(solution, path=\"\/tmp\/test_output.h5\")\n    \n    # Verify output is readable\n    readback = h5py.File(output_path, \"r\")\n    assert \"solution\" in readback.keys()\n<\/code><\/pre>\n<h2>Moquerie avanc\u00e9e pour les biblioth\u00e8ques scientifiques<\/h2>\n<h3>Le d\u00e9fi<\/h3>\n<p>Le test de code scientifique peut \u00eatre lent, car les fonctions NumPy, Scipy et Fipy peuvent avoir des types de retour complexes et des chemins d'ex\u00e9cution co\u00fbteux. Les moquer vous permet de tester rapidement la logique de l'encapsuleur.<\/p>\n<pre><code class=\"language-python\">import numpy as np\nfrom unittest.mock import patch\n\ndef test_solver_wrapper():\n    \"\"\"Test that your wrapper calls scipy.sparse.linalg correctly.\"\"\"\n    \n    with patch(\"scipy.sparse.linalg.spsolve\") as mock_solve:\n        mock_solve.return_value = np.zeros(100)\n        \n        result = your_solver_wrapper(mesh_size=100)\n        \n        mock_solve.assert_called_once()\n        call_args = mock_solve.call_args\n        assert isinstance(call_args[0], np.ndarray)\n        assert isinstance(call_args[1], np.ndarray)\n<\/code><\/pre>\n<p>Cela v\u00e9rifie la logique d'encapsulation sans attendre l'ex\u00e9cution du solveur r\u00e9el. Le test peut se terminer en millisecondes au lieu de secondes.<\/p>\n<h3>Test des allers-retours pour les donn\u00e9es scientifiques<\/h3>\n<p>Le test que les donn\u00e9es scientifiques survivent \u00e0 un cycle lecture-\u00e9criture-lecture est un mod\u00e8le puissant.<\/p>\n<pre><code class=\"language-python\">from hypothesis import given, strategies as st\n\n@given(st.integers(min_value=10, max_value=200))\ndef test_hdf5_roundtrip(n_cells):\n    \"\"\"Writing and reading HDF5 data should preserve values.\"\"\"\n    mesh = create_mesh(n_cells)\n    solution = solve(mesh, dt=0.01)\n    \n    path = write_to_hdf5(solution, path=\"\/tmp\/test_roundtrip.h5\")\n    \n    readback = read_from_hdf5(path)\n    \n    assert np.allclose(readback, solution, rtol=1e-6, atol=1e-8)\n<\/code><\/pre>\n<p>Cela permet de d\u00e9tecter les bogues de s\u00e9rialisation des donn\u00e9es, les incompatibilit\u00e9s de type DTYPE et les probl\u00e8mes de segmentation HDF5 qui pourraient silencieusement des r\u00e9sultats corrompus.<\/p>\n<h2>Reproductibilit\u00e9 des tests<\/h2>\n<h3>Gestion des semences d\u00e9terministes<\/h3>\n<p>Les op\u00e9rations \u00e0 virgule flottante peuvent \u00eatre d\u00e9terministes lorsque la graine est fixe, mais la reproductibilit\u00e9 n\u00e9cessite \u00e9galement des versions de biblioth\u00e8que coh\u00e9rentes, des indicateurs de compilation et des mod\u00e8les de d\u00e9composition MPI.<\/p>\n<pre><code class=\"language-python\">def test_reproducible_seed():\n    \"\"\"Fixed seed should produce identical results.\"\"\"\n    np.random.seed(42)\n    \n    mesh1 = generate_mesh(seed=42)\n    solution1 = solve(mesh1)\n    \n    np.random.seed(42)\n    mesh2 = generate_mesh(seed=42)\n    solution2 = solve(mesh2)\n    \n    assert np.allclose(solution1, solution2)\n<\/code><\/pre>\n<h3>Essais \u00e0 virgule flottante multiplateforme<\/h3>\n<p>Diff\u00e9rentes plates-formes peuvent produire des r\u00e9sultats \u00e0 virgule flottante l\u00e9g\u00e8rement diff\u00e9rents en raison des diff\u00e9rences de mat\u00e9riel et de biblioth\u00e8que.<\/p>\n<pre><code class=\"language-python\">def test_floating_point_stability():\n    \"\"\"Results should be consistent across platforms within tolerance.\"\"\"\n    mesh = create_mesh(64)\n    solution = solve(mesh)\n    \n    # All values should be finite\n    assert np.all(np.isfinite(solution))\n    \n    # Values should not be suspiciously small\n    assert np.all(np.abs(solution) &gt; np.finfo(np.float64).tiny * 1e-3)\n<\/code><\/pre>\n<h2>Ce que nous recommandons : la strat\u00e9gie de test<\/h2>\n<p>Pour un projet Python scientifique de production, utilisez une strat\u00e9gie de test en couches.<\/p>\n<h3>Doit avoir<\/h3>\n<ol>\n<li>Tests unitaires avec <code>pytest.approx<\/code> pour les assertions num\u00e9riques.<\/li>\n<li>Appareils param\u00e9tr\u00e9s pour des tests syst\u00e9matiques \u00e0 travers les param\u00e8tres.<\/li>\n<li>Tests de convergence qui v\u00e9rifient le comportement du solveur \u00e0 travers le raffinement.<\/li>\n<\/ol>\n<h3>Aurait d\u00fb<\/h3>\n<ol start=\"4\">\n<li>Hypoth\u00e8se Tests bas\u00e9s sur la propri\u00e9t\u00e9 pour les cas de bord et la robustesse num\u00e9rique.<\/li>\n<li>Tests d'int\u00e9gration pour les flux de travail de simulation en plusieurs \u00e9tapes.<\/li>\n<li>Strat\u00e9gies de moquerie pour les d\u00e9pendances externes et les interfaces de solveur.<\/li>\n<\/ol>\n<h3>Agr\u00e9able d'avoir<\/h3>\n<ol start=\"7\">\n<li>Tests de reproductibilit\u00e9 multi-plateformes.<\/li>\n<li>Data roundtrip teste qui \u00e9crit, lise et v\u00e9rifie les donn\u00e9es scientifiques.<\/li>\n<li>Benchmarks de performance avec <code>pytest-benchmark<\/code>.<\/li>\n<\/ol>\n<h2>erreurs courantes<\/h2>\n<h3>Erreur&nbsp;1&nbsp;: utilisation incorrecte des assertions d\u00e9pendantes de la tol\u00e9rance<\/h3>\n<p>N'\u00e9crivez pas de tests num\u00e9riques comme <code>assert result == expected<\/code>. Utilisez <code>pytest.approx<\/code> ou les assertions sensibles \u00e0 la tol\u00e9rance pour le code num\u00e9rique. La tol\u00e9rance d\u00e9pend de l'\u00e9chelle du probl\u00e8me, donc une tol\u00e9rance fixe peut \u00e9chouer pour des probl\u00e8mes plus importants.<\/p>\n<h3>Erreur&nbsp;2&nbsp;: ignorer la convergence<\/h3>\n<p>Si le code utilise des solveurs it\u00e9ratifs, n'ignorez pas les tests de convergence. Un solveur qui fonctionne sur un maillage peut diverger sur un autre. Testez le comportement de convergence, pas seulement le r\u00e9sultat final.<\/p>\n<h3>Erreur&nbsp;3&nbsp;: Tester uniquement le chemin heureux<\/h3>\n<p>Les tests bas\u00e9s sur des propri\u00e9t\u00e9s vous obligent \u00e0 tester des cas de bord que les tests manuels manquent souvent. Ajoutez au moins un test d'hypoth\u00e8se pour chaque fonction num\u00e9rique majeure. Il peut r\u00e9v\u00e9ler des bogues que les tests normaux bas\u00e9s sur des exemples n'attrapent jamais.<\/p>\n<h2>R\u00e9sum\u00e9<\/h2>\n<p>Les tests unitaires sont n\u00e9cessaires, mais ils ne sont pas suffisants pour le code scientifique. Les tests bas\u00e9s sur des propri\u00e9t\u00e9s, les tests de convergence, les flux de travail d'int\u00e9gration et les contr\u00f4les de reproductibilit\u00e9 r\u00e9pondent aux d\u00e9fis sp\u00e9cifiques que les simulations num\u00e9riques introduisent.<\/p>\n<p>L'id\u00e9e cl\u00e9 est que les tests scientifiques ne consistent pas seulement \u00e0 v\u00e9rifier que le code produit une bonne r\u00e9ponse. Il s'agit de v\u00e9rifier que le code se comporte correctement. Il doit converger, conserver, adapter et g\u00e9rer les cas de bordure avec \u00e9l\u00e9gance.<\/p>\n<p>Commencez par les mod\u00e8les incontournables, puis ajoutez les mod\u00e8les de devrait avoir au fur et \u00e0 mesure que la suite de tests m\u00fbrit. Utilisez les mod\u00e8les Nice-to-Have pour durcir la base de code au fil du temps.<\/p>\n<p>Les tests bas\u00e9s sur des propri\u00e9t\u00e9s avec Hypothesis sont l'un des ajouts les plus \u00e9lev\u00e9s que vous puissiez faire, car ils attrapent les bogues que les tests manuels manquent souvent.<\/p>\n<h2>Guides connexes<\/h2>\n<ul>\n<li><a href=\"https:\/\/matforge.org\/unit-testing-scientific-code-pytest-strategies-research-projects\/\">Tests unitaires pour le code scientifique&nbsp;: strat\u00e9gies PyTest pour des projets de recherche<\/a> - la fondation dont vous avez besoin avant cet article.<\/li>\n<li><a href=\"https:\/\/matforge.org\/when-to-use-fem-fvm-fdm\/\">Quand utiliser FEM, FVM ou FDM&nbsp;: une comparaison pratique pour les d\u00e9butants<\/a> \u2014 Contexte sur les m\u00e9thodes num\u00e9riques.<\/li>\n<li><a href=\"https:\/\/matforge.org\/mesh-quality-convergence-studies-guide-2\/\">\u00c9tudes de qualit\u00e9 et de convergence du maillage&nbsp;: un guide pratique pour les simulations scientifiques<\/a> \u2014 contexte de test de convergence connexe.<\/li>\n<\/ul>\n<h2>Que faire ensuite<\/h2>\n<p>Si vous venez de commencer \u00e0 \u00e9crire des tests de code scientifique, commencez par les mod\u00e8les incontournables ci-dessus. Une fois ceux-ci en place, ajoutez des tests d'hypoth\u00e8se pour les fonctions num\u00e9riques les plus importantes, en particulier les fonctions appel\u00e9es \u00e0 plusieurs reprises dans les simulations.<\/p>\n<p>Pour les \u00e9quipes travaillant sur des logiciels scientifiques de production, l'investissement dans des tests bas\u00e9s sur la propri\u00e9t\u00e9 peut \u00eatre payant rapidement. Il s'agit de la m\u00eame strat\u00e9gie utilis\u00e9e par les principales biblioth\u00e8ques scientifiques Python et l'un des moyens les plus efficaces de trouver des bogues de cas de bord dans le code num\u00e9rique.<\/p>\n<p>Si vous avez besoin d'aide pour concevoir une strat\u00e9gie de test pour un projet de simulation, envisagez de r\u00e9server une consultation. Un examen pratique peut associer votre structure de code aux tests unitaires, aux tests de convergence, aux tests d'int\u00e9gration et aux contr\u00f4les de reproductibilit\u00e9.<\/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\"> 9<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span>Points \u00e0 retenir cl\u00e9s Les bases de Pytest couvrent les tests unitaires. Les luminaires, la param\u00e9trisation et pytest.approx sont votre point de d\u00e9part, pas la ligne d&rsquo;arriv\u00e9e. Les tests bas\u00e9s sur la propri\u00e9t\u00e9 avec une hypoth\u00e8se attrapent des cas de bord que les tests manuels manquent souvent. Il est utilis\u00e9 par les principaux projets scientifiques [&hellip;]<\/p>\n","protected":false,"raw":""},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_locale":"fr_FR","_original_post":"https:\/\/matforge.org\/?p=365","iawp_total_views":1,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1258","post","type-post","status-publish","format-standard","hentry","category-simulation-modeling-projects","fr-FR"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Mod\u00e8les de test Python pour le code scientifique<\/title>\n<meta name=\"description\" content=\"Apprenez des mod\u00e8les de test Python avanc\u00e9s pour le code scientifique, notamment des tests d&#039;hypoth\u00e8se, de convergence, de tests d&#039;int\u00e9gration, de moquerie et de contr\u00f4le de reproductibilit\u00e9\" \/>\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\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Mod\u00e8les de test Python pour le code scientifique\" \/>\n<meta property=\"og:description\" content=\"Apprenez des mod\u00e8les de test Python avanc\u00e9s pour le code scientifique, notamment des tests d&#039;hypoth\u00e8se, de convergence, de tests d&#039;int\u00e9gration, de moquerie et de contr\u00f4le de reproductibilit\u00e9\" \/>\n<meta property=\"og:url\" content=\"https:\/\/matforge.org\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/\" \/>\n<meta property=\"og:site_name\" content=\"matforge.org\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-21T14:31:42+00:00\" \/>\n<meta name=\"author\" content=\"Elena Markovska\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"Elena Markovska\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"15 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/\"},\"author\":{\"name\":\"Elena Markovska\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/980162bb5de46742daece973661d93da\"},\"headline\":\"Mod\u00e8les de test Python pour le code scientifique : Beyond Pytest Basics\",\"datePublished\":\"2026-08-21T14:31:42+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/\"},\"wordCount\":2147,\"commentCount\":0,\"articleSection\":[\"Simulation &amp; Projets de mod\u00e9lisation\"],\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/matforge.org\\\/fr\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/\",\"url\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/\",\"name\":\"Mod\u00e8les de test Python pour le code scientifique\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/#website\"},\"datePublished\":\"2026-08-21T14:31:42+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/980162bb5de46742daece973661d93da\"},\"description\":\"Apprenez des mod\u00e8les de test Python avanc\u00e9s pour le code scientifique, notamment des tests d'hypoth\u00e8se, de convergence, de tests d'int\u00e9gration, de moquerie et de contr\u00f4le de reproductibilit\u00e9\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/matforge.org\\\/fr\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/matforge.org\\\/fr\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/matforge.org\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Mod\u00e8les de test Python pour le code scientifique : Beyond Pytest Basics\"}]},{\"@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\":\"fr-FR\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/980162bb5de46742daece973661d93da\",\"name\":\"Elena Markovska\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2de4e35b6581d7d8a839335156c6b5834cfaa0aef537a1c837e882dc57eea1e7?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2de4e35b6581d7d8a839335156c6b5834cfaa0aef537a1c837e882dc57eea1e7?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2de4e35b6581d7d8a839335156c6b5834cfaa0aef537a1c837e882dc57eea1e7?s=96&d=mm&r=g\",\"caption\":\"Elena Markovska\"},\"sameAs\":[\"http:\\\/\\\/matforge.org\"],\"url\":\"https:\\\/\\\/matforge.org\\\/author\\\/elena-markovska\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Mod\u00e8les de test Python pour le code scientifique","description":"Apprenez des mod\u00e8les de test Python avanc\u00e9s pour le code scientifique, notamment des tests d'hypoth\u00e8se, de convergence, de tests d'int\u00e9gration, de moquerie et de contr\u00f4le de reproductibilit\u00e9","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\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/","og_locale":"fr_FR","og_type":"article","og_title":"Mod\u00e8les de test Python pour le code scientifique","og_description":"Apprenez des mod\u00e8les de test Python avanc\u00e9s pour le code scientifique, notamment des tests d'hypoth\u00e8se, de convergence, de tests d'int\u00e9gration, de moquerie et de contr\u00f4le de reproductibilit\u00e9","og_url":"https:\/\/matforge.org\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/","og_site_name":"matforge.org","article_published_time":"2026-08-21T14:31:42+00:00","author":"Elena Markovska","twitter_card":"summary_large_image","twitter_misc":{"\u00c9crit par":"Elena Markovska","Dur\u00e9e de lecture estim\u00e9e":"15 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/matforge.org\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/#article","isPartOf":{"@id":"https:\/\/matforge.org\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/"},"author":{"name":"Elena Markovska","@id":"https:\/\/matforge.org\/#\/schema\/person\/980162bb5de46742daece973661d93da"},"headline":"Mod\u00e8les de test Python pour le code scientifique : Beyond Pytest Basics","datePublished":"2026-08-21T14:31:42+00:00","mainEntityOfPage":{"@id":"https:\/\/matforge.org\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/"},"wordCount":2147,"commentCount":0,"articleSection":["Simulation &amp; Projets de mod\u00e9lisation"],"inLanguage":"fr-FR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/matforge.org\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/matforge.org\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/","url":"https:\/\/matforge.org\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/","name":"Mod\u00e8les de test Python pour le code scientifique","isPartOf":{"@id":"https:\/\/matforge.org\/#website"},"datePublished":"2026-08-21T14:31:42+00:00","author":{"@id":"https:\/\/matforge.org\/#\/schema\/person\/980162bb5de46742daece973661d93da"},"description":"Apprenez des mod\u00e8les de test Python avanc\u00e9s pour le code scientifique, notamment des tests d'hypoth\u00e8se, de convergence, de tests d'int\u00e9gration, de moquerie et de contr\u00f4le de reproductibilit\u00e9","breadcrumb":{"@id":"https:\/\/matforge.org\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/matforge.org\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/matforge.org\/fr\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/matforge.org\/"},{"@type":"ListItem","position":2,"name":"Mod\u00e8les de test Python pour le code scientifique : Beyond Pytest Basics"}]},{"@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":"fr-FR"},{"@type":"Person","@id":"https:\/\/matforge.org\/#\/schema\/person\/980162bb5de46742daece973661d93da","name":"Elena Markovska","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/secure.gravatar.com\/avatar\/2de4e35b6581d7d8a839335156c6b5834cfaa0aef537a1c837e882dc57eea1e7?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/2de4e35b6581d7d8a839335156c6b5834cfaa0aef537a1c837e882dc57eea1e7?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/2de4e35b6581d7d8a839335156c6b5834cfaa0aef537a1c837e882dc57eea1e7?s=96&d=mm&r=g","caption":"Elena Markovska"},"sameAs":["http:\/\/matforge.org"],"url":"https:\/\/matforge.org\/author\/elena-markovska\/"}]}},"_links":{"self":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/1258","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\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/comments?post=1258"}],"version-history":[{"count":1,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/1258\/revisions"}],"predecessor-version":[{"id":1432,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/1258\/revisions\/1432"}],"wp:attachment":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/media?parent=1258"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/categories?post=1258"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/tags?post=1258"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}