{"id":854,"date":"2026-07-30T12:22:24","date_gmt":"2026-07-30T12:22:24","guid":{"rendered":"https:\/\/matforge.org\/?p=854","raw":"https:\/\/matforge.org\/?p=854"},"modified":"2026-07-30T12:22:24","modified_gmt":"2026-07-30T12:22:24","slug":"python-testing-patterns-for-scientific-code-beyond-pytest-basics","status":"publish","type":"post","link":"https:\/\/matforge.org\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/","title":{"rendered":"Python-Testmuster f\u00fcr wissenschaftlichen Code: Beyond PyTest Basics","raw":"Python-Testmuster f\u00fcr wissenschaftlichen Code: 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\"> 7<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span><h2>Schl\u00fcssel zum Mitnehmen<\/h2>\n<ul>\n<li>Pytest-Grundlagen decken Einheitentest ab. Fixtures, Parametrisierung und <code>pytest.approx<\/code> sind Ihr Ausgangspunkt, nicht die Ziellinie.<\/li>\n<li>Eigenschaftenbasierte Tests mit Hypothese fangen Randf\u00e4lle ab, die manuelle Tests h\u00e4ufig \u00fcbersehen. Es wird von wichtigen wissenschaftlichen Python-Projekten wie Numpy, Jax und Pytorch verwendet.<\/li>\n<li>Numerische Konvergenztests erfordern Solver-spezifische Toleranzstrategien. Standard-Behauptungen erfassen kein Konvergenzverhalten.<\/li>\n<li>Integrationstests f\u00fcr Simulations-Workflows erfordern Spottmuster, die externe Abh\u00e4ngigkeiten isolieren und gleichzeitig die numerische Logik beibehalten.<\/li>\n<li>Reproduzierbarkeitstests gehen \u00fcber Unit-Tests hinaus. Sie ben\u00f6tigen ein deterministisches Saatgutmanagement und eine plattform\u00fcbergreifende Gleitkommaverifizierung.<\/li>\n<\/ul>\n<h2>Was Sie zuerst wissen sollten<\/h2>\n<p>Wenn Sie die Grundlagen des Unit-Testings f\u00fcr wissenschaftlichen Code bereits kennen, verstehen Sie wahrscheinlich, wie man eine <code>test_<\/code>-Funktion schreibt, wann Fixtures verwendet werden und wie <code>pytest.approx<\/code> Gleitkommavergleiche handhabt. Das ist die Grundlinie.<\/p>\n<p>Der wissenschaftliche Kodex stellt Herausforderungen vor, die die Standard-Einheitentests nicht vollst\u00e4ndig angehen. Numerische Algorithmen konvergieren oder konvergieren nicht. Simulationen h\u00e4ngen von externen L\u00f6sern und I\/O-Systemen ab. Flie\u00dfkommaarithmetik kann sich \u00fcber Plattformen hinweg unterschiedlich verhalten.<\/p>\n<p>Randf\u00e4lle, die in normalen Daten niemals auftreten, wie z.<\/p>\n<p>Dieser Artikel behandelt Testmuster f\u00fcr wissenschaftlichen Python-Code \u00fcber die Grundlagen hinaus.<\/p>\n<h2>Die Testpyramide f\u00fcr wissenschaftlichen Code<\/h2>\n<p>Jede Teststrategie hat einen Platz auf der Pyramide. F\u00fcr den wissenschaftlichen Code sieht die Struktur wie folgt aus:<\/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>Unit-Tests bilden die Grundlage. \u00dcber ihnen befinden sich eigenschaftsbasierte Tests, Integrationstests und Simulationstests auf Systemebene. Diese h\u00f6heren Schichten machen das Testen f\u00fcr echte wissenschaftliche Arbeitsabl\u00e4ufe n\u00fctzlich.<\/p>\n<h2>Eigenschaftenbasiertes Testen mit Hypothese<\/h2>\n<h3>Warum eigenschaftsbasierte Tests f\u00fcr wissenschaftlichen Code wichtig sind<\/h3>\n<p>Hypothese ist nicht nur ein Lehrmittel. Es wird von gro\u00dfen wissenschaftlichen Python-Bibliotheken verwendet, um Fehler in numerischen Kernroutinen zu finden.<\/p>\n<p>Eigenschaftenbasierte Tests ver\u00e4ndern das mentale Modell des Tests. Anstatt Tests mit einer bestimmten Eingabe und einer erwarteten Ausgabe zu schreiben, schreiben Sie Tests, die die mathematischen Eigenschaften \u00fcberpr\u00fcfen. Die Hypothese generiert dann automatisch viele Eingaben, einschlie\u00dflich Kantenf\u00e4llen, die Sie m\u00f6glicherweise nicht manuell testen m\u00f6chten.<\/p>\n<h3>Konvertieren eines handgeschriebenen Tests in eigenschaftsbasierte<\/h3>\n<p>Betrachten Sie diesen Test f\u00fcr einen numerischen Solver-Wrapper:<\/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>Dieser Test \u00fcberpr\u00fcft eine Maschengr\u00f6\u00dfe und einen Zeitschritt. Es kann sogar passieren, wenn der Solver f\u00fcr gr\u00f6\u00dfere Netze oder unterschiedliche Zeitschrittwerte ausf\u00e4llt.<\/p>\n<p>Hier eine Hypothesenversion:<\/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>Dies f\u00e4ngt Probleme auf, die ein handgeschriebener Test \u00fcbersehen kann:<\/p>\n<ul>\n<li>Eckf\u00e4lle in der Netzgenerierung, einschlie\u00dflich d\u00fcnner Dom\u00e4nen und extremer Seitenverh\u00e4ltnisse.<\/li>\n<li>Zeitschrittsensitivit\u00e4t, insbesondere wenn gro\u00dfe <code>dt<\/code> -Werte explizite Schemata destabilisieren.<\/li>\n<li>Gleitkomma-Kantenf\u00e4lle, einschlie\u00dflich Rundungsgrenzen und ungew\u00f6hnliche numerische Bereiche.<\/li>\n<\/ul>\n<h3>Die Eigenschafts-Checkliste f\u00fcr wissenschaftliche Funktionen<\/h3>\n<p>Identifizieren Sie vor dem Schreiben eines Eigenschaftstests, welche Eigenschaften die Funktion erf\u00fcllen soll.<\/p>\n<table class=\"custom-table\">\n<tbody>\n<tr>\n<th>Eigenschaftstyp<\/th>\n<th>Beispiel<\/th>\n<th>Warum es wichtig ist<\/th>\n<\/tr>\n<tr>\n<td>Erhaltung<\/td>\n<td>Masse, Energie oder Impuls bleiben erhalten<\/td>\n<td>Verhindert numerische Drift<\/td>\n<\/tr>\n<tr>\n<td>Symmetrie<\/td>\n<td><code>f(a, b) == f(b, a)<\/code> f\u00fcr symmetrische Operatoren<\/td>\n<td>F\u00e4ngt asymmetrische Implementierungsfehler ab<\/td>\n<\/tr>\n<tr>\n<td>Skalieren<\/td>\n<td><code>f(k*a, k*b) == k^n * f(a, b)<\/code> f\u00fcr homogene Funktionen<\/td>\n<td>Tests die dimensionale Konsistenz<\/td>\n<\/tr>\n<tr>\n<td>Monotonie<\/td>\n<td>Die Temperatur sinkt mit der Entfernung in einem einfachen Hitzeproblem<\/td>\n<td>\u00dcberpr\u00fcft die k\u00f6rperliche Korrektheit<\/td>\n<\/tr>\n<tr>\n<td>Grenzen<\/td>\n<td>Werte bleiben in einem g\u00fcltigen Bereich<\/td>\n<td>Verhindert nicht-physische Ergebnisse<\/td>\n<\/tr>\n<tr>\n<td>Null-Verhalten<\/td>\n<td><code>f(0)<\/code> Gibt den erwarteten Flanken-Case-Wert zur\u00fcck<\/td>\n<td>Schutz gegen Division durch null und einzelne F\u00e4lle<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Numerische Konvergenzpr\u00fcfung<\/h2>\n<h3>Warum Konvergenztests anders ist<\/h3>\n<p>Einheitentests pr\u00fcfen, ob Code eine bestimmte Antwort erzeugt. Konvergenztests pr\u00fcfen, ob der Code bei der Verfeinerung der Parameter der richtigen Antwort n\u00e4hert.<\/p>\n<p>Dies ist entscheidend f\u00fcr iterative L\u00f6ser, Zeitschrittschemata und Netzverfeinerungsstudien. Standard-Behauptungen wie <code>assert result == expected<\/code> erfassen kein Konvergenzverhalten. Sie m\u00fcssen Konvergenzmuster testen.<\/p>\n<h3>Testen der Solver-Konvergenz<\/h3>\n<p>Verschiedene Solver-Suiten verwenden unterschiedliche Konvergenzkriterien. Der entscheidende Punkt ist, dass die Konvergenzkriterien solverspezifisch sind und die Standardtoleranz m\u00f6glicherweise nicht f\u00fcr jedes Problem geeignet ist.<\/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>Toleranzstrategien<\/h3>\n<p>Tests sollten die g\u00e4ngigsten Toleranzkriterien abdecken, die vom Solver-Stack verwendet werden.<\/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>Divergenzf\u00e4lle testen<\/h3>\n<p>Sie sollten auch testen, dass der Code die Abweichung ordnungsgem\u00e4\u00df behandelt.<\/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>Integrationstests f\u00fcr Simulationsworkflows<\/h2>\n<h3>Warum Integrationstests wichtig sind<\/h3>\n<p>Unit-Tests \u00fcberpr\u00fcfen einzelne Funktionen. Integrationstests stellen sicher, dass die Funktionen korrekt zusammenarbeiten.<\/p>\n<p>F\u00fcr den wissenschaftlichen Code bedeutet dies das Testen von mehrstufigen Simulationen, Datenpipelines, Solver-Kopplungsmustern und E \/ A-Workflows.<\/p>\n<h3>Muster 1: Verspotten von externen Abh\u00e4ngigkeiten im wissenschaftlichen Kodex<\/h3>\n<p>Beim Testen von Funktionen, die externe APIs, Datenbanken oder Dateisysteme aufrufen, verwenden Sie Spott, um die numerische Logik zu isolieren.<\/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>Dies isoliert die Datenaufbereitungslogik aus der HDF5-Datei Write. Der Test \u00fcberpr\u00fcft, ob Ihre Code-Formate und -pakete korrekt sind, ohne auf E \/ A zu warten.<\/p>\n<h3>Muster 2: Parametrisierte Fixtures f\u00fcr Simulations-Sweeps<\/h3>\n<p>Anstatt einige Netzgr\u00f6\u00dfen manuell zu testen, verwenden Sie parametrisierte Vorrichtungen f\u00fcr systematische Sweeps.<\/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>Muster 3: Testen von mehrstufigen Workflows<\/h3>\n<p>Wissenschaftliche Simulationen verf\u00fcgen h\u00e4ufig \u00fcber mehrstufige Pipelines: Generierung, L\u00f6sung, Nachbearbeitung und Schreibausgabe.<\/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>Fortgeschrittenes Verspotten f\u00fcr wissenschaftliche Bibliothek<\/h2>\n<h3>Herausforderung<\/h3>\n<p>Das Testen von wissenschaftlichem Code kann langsam sein, da numpy-, scipy- und fipy-Funktionen komplexe R\u00fcckgabetypen und teure Ausf\u00fchrungspfade haben k\u00f6nnen. Wenn Sie sie verspotten, k\u00f6nnen Sie die Wrapper-Logik schnell testen.<\/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>Dadurch wird die Wrapper-Logik \u00fcberpr\u00fcft, ohne dass der tats\u00e4chliche Solver ausgef\u00fchrt wird. Der Test kann in Millisekunden statt Sekunden beendet werden.<\/p>\n<h3>Testen von Hin- und R\u00fcckfahrten f\u00fcr wissenschaftliche Daten<\/h3>\n<p>Das Testen, dass wissenschaftliche Daten einen Lese-Schreib-Lese-Zyklus \u00fcberstehen, ist ein leistungsstarkes Muster.<\/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>Dadurch werden Datenserialisierungsfehler, DType-Fehlanpassungen und HDF5-Chunking-Probleme erfasst, die die Ergebnisse stillschweigend besch\u00e4digen k\u00f6nnten.<\/p>\n<h2>Reproduzierbarkeit testen<\/h2>\n<h3>Deterministisches Saatgutmanagement<\/h3>\n<p>Gleitkommaoperationen k\u00f6nnen deterministisch sein, wenn der Seed festgelegt ist, aber die Reproduzierbarkeit erfordert auch konsistente Bibliotheksversionen, Kompilierungsflags und MPI-Dekompositionsmuster.<\/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>Plattform\u00fcbergreifende Gleitkomma-Tests<\/h3>\n<p>Verschiedene Plattformen k\u00f6nnen aufgrund von Hardware- und Bibliotheksunterschieden leicht unterschiedliche Gleitkommaergebnisse erzeugen.<\/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>Was wir empfehlen: Die Teststrategie<\/h2>\n<p>Verwenden Sie f\u00fcr ein produktionswissenschaftliches Python-Projekt eine geschichtete Teststrategie.<\/p>\n<h3>Haben m\u00fcssen<\/h3>\n<ol>\n<li>Unit-Tests mit <code>pytest.approx<\/code> f\u00fcr numerische Behauptungen.<\/li>\n<li>Parametrisierte Ger\u00e4te f\u00fcr systematische Tests \u00fcber Parameter hinweg.<\/li>\n<li>Konvergenztests, die das Solververhalten w\u00e4hrend der Verfeinerung \u00fcberpr\u00fcfen.<\/li>\n<\/ol>\n<h3>Sollte haben<\/h3>\n<ol start=\"4\">\n<li>Hypotheseneigenschaftsbasierte Tests f\u00fcr Randf\u00e4lle und numerische Robustheit.<\/li>\n<li>Integrationstests f\u00fcr mehrstufige Simulationsworkflows.<\/li>\n<li>Mocking-Strategien f\u00fcr externe Abh\u00e4ngigkeiten und Solver-Schnittstellen.<\/li>\n<\/ol>\n<h3>Sch\u00f6n zu haben<\/h3>\n<ol start=\"7\">\n<li>Plattform\u00fcbergreifende Reproduzierbarkeitstests.<\/li>\n<li>Daten-Rundtrip-Tests, die wissenschaftliche Daten schreiben, lesen und \u00fcberpr\u00fcfen.<\/li>\n<li>Leistungsbenchmarks mit <code>pytest-benchmark<\/code>.<\/li>\n<\/ol>\n<h2>H\u00e4ufige Fehler<\/h2>\n<h3>Fehler 1: Toleranzabh\u00e4ngige Aussagen falsch verwenden<\/h3>\n<p>Schreiben Sie keine numerischen Tests wie <code>assert result == expected<\/code>. Verwenden Sie <code>pytest.approx<\/code> oder toleranzbewusste Zusicherungen f\u00fcr numerischen Code. Die Toleranz h\u00e4ngt von der Problemskala ab, so dass eine feste Toleranz bei gr\u00f6\u00dferen Problemen fehlschl\u00e4gt.<\/p>\n<h3>Fehler 2: Konvergenz ignorieren<\/h3>\n<p>Wenn der Code iterative Solver verwendet, \u00fcberspringen Sie keine Konvergenztests. Ein Solver, der auf einem Netz arbeitet, kann auf einem anderen divergieren. Testen Sie das Konvergenzverhalten, nicht nur das Endergebnis.<\/p>\n<h3>Fehler 3: Nur den gl\u00fccklichen Weg testen<\/h3>\n<p>Property-basierte Tests zwingen Sie dazu, Randf\u00e4lle zu testen, die manuelle Tests h\u00e4ufig \u00fcbersehen. F\u00fcgen Sie mindestens einen Hypothesentest f\u00fcr jede wichtige numerische Funktion hinzu. Es kann Fehler aufdecken, die normale beispielbasierte Tests niemals abfangen.<\/p>\n<h2>Zusammenfassung<\/h2>\n<p>Unit-Tests sind notwendig, aber sie reichen f\u00fcr den wissenschaftlichen Kodex nicht aus. Property-basierte Tests, Konvergenztests, Integrationsworkflows und Reproduzierbarkeitspr\u00fcfungen befassen sich mit den spezifischen Herausforderungen, die numerische Simulationen mit sich bringen.<\/p>\n<p>Die wichtigste Erkenntnis ist, dass es bei wissenschaftlichen Tests nicht nur darum geht, zu \u00fcberpr\u00fcfen, ob Code eine richtige Antwort liefert. Es geht darum zu \u00fcberpr\u00fcfen, ob sich der Code richtig verh\u00e4lt. Es sollte elegant konvergieren, konservieren, skalieren und bearbeiten.<\/p>\n<p>Beginnen Sie mit den Must-Have-Mustern und f\u00fcgen Sie dann die Muster hinzu, wenn die Testsuite ausgereift ist. Verwenden Sie die sch\u00f6nen Muster, um die Codebasis im Laufe der Zeit zu h\u00e4rten.<\/p>\n<p>Property-basierte Tests mit Hypothese sind eine der hochwertigsten Erg\u00e4nzungen, die Sie vornehmen k\u00f6nnen, da sie Fehler auffangen, die manuelle Tests h\u00e4ufig \u00fcbersehen.<\/p>\n<h2>Verwandte Anleitungen<\/h2>\n<ul>\n<li><a href=\"https:\/\/matforge.org\/unit-testing-scientific-code-pytest-strategies-research-projects\/\"> Einheitentest f\u00fcr wissenschaftlichen Kodex: PYTest-Strategien f\u00fcr Forschungsprojekte <\/a> &#8211; Die Grundlage, die Sie vor diesem Artikel ben\u00f6tigen.<\/li>\n<li><a href=\"https:\/\/matforge.org\/when-to-use-fem-fvm-fdm\/\">Wann verwendet werden FEM, FVM oder FDM: Ein praktischer Vergleich f\u00fcr Anf\u00e4nger <\/a> &#8211; Kontext zu numerischen Methoden.<\/li>\n<li><a href=\"https:\/\/matforge.org\/mesh-quality-convergence-studies-guide-2\/\">Mesh-Qualit\u00e4ts- und Konvergenzstudien: Ein praktischer Leitfaden f\u00fcr wissenschaftliche Simulationen <\/a> &#8211; Verwandter Konvergenztestkontext.<\/li>\n<\/ul>\n<h2>Was macht man als n\u00e4chstes<\/h2>\n<p>Wenn Sie gerade angefangen haben, Tests f\u00fcr wissenschaftlichen Code zu schreiben, beginnen Sie mit den oben genannten Mustern. Sobald diese vorhanden sind, f\u00fcgen Sie Hypothesentests f\u00fcr die wichtigsten numerischen Funktionen hinzu, insbesondere Funktionen, die in Simulationen wiederholt aufgerufen werden.<\/p>\n<p>F\u00fcr Teams, die an produktiven wissenschaftlichen Software arbeiten, kann sich die Investition in objektbasierte Tests schnell auszahlen. Es handelt sich um dieselbe Strategie, die von gro\u00dfen wissenschaftlichen Python-Bibliotheken verwendet wird, und ist eine der effektivsten M\u00f6glichkeiten, um Edge-Case-Fehler im numerischen Code zu finden.<\/p>\n<p>Wenn Sie Hilfe beim Entwerfen einer Teststrategie f\u00fcr ein Simulationsprojekt ben\u00f6tigen, sollten Sie eine Beratung buchen. Eine praktische \u00dcberpr\u00fcfung kann Ihre Codestruktur auf Einheitentests, Konvergenztests, Integrationstests und Reproduzierbarkeitspr\u00fcfungen abbilden.<\/p>\n","protected":false,"raw":"<h2>Schl\u00fcssel zum Mitnehmen<\/h2>\n<ul>\n<li>Pytest-Grundlagen decken Einheitentest ab. Fixtures, Parametrisierung und <code>pytest.approx<\/code> sind Ihr Ausgangspunkt, nicht die Ziellinie.<\/li>\n<li>Eigenschaftenbasierte Tests mit Hypothese fangen Randf\u00e4lle ab, die manuelle Tests h\u00e4ufig \u00fcbersehen. Es wird von wichtigen wissenschaftlichen Python-Projekten wie Numpy, Jax und Pytorch verwendet.<\/li>\n<li>Numerische Konvergenztests erfordern Solver-spezifische Toleranzstrategien. Standard-Behauptungen erfassen kein Konvergenzverhalten.<\/li>\n<li>Integrationstests f\u00fcr Simulations-Workflows erfordern Spottmuster, die externe Abh\u00e4ngigkeiten isolieren und gleichzeitig die numerische Logik beibehalten.<\/li>\n<li>Reproduzierbarkeitstests gehen \u00fcber Unit-Tests hinaus. Sie ben\u00f6tigen ein deterministisches Saatgutmanagement und eine plattform\u00fcbergreifende Gleitkommaverifizierung.<\/li>\n<\/ul>\n<h2>Was Sie zuerst wissen sollten<\/h2>\n<p>Wenn Sie die Grundlagen des Unit-Testings f\u00fcr wissenschaftlichen Code bereits kennen, verstehen Sie wahrscheinlich, wie man eine <code>test_<\/code>-Funktion schreibt, wann Fixtures verwendet werden und wie <code>pytest.approx<\/code> Gleitkommavergleiche handhabt. Das ist die Grundlinie.<\/p>\n<p>Der wissenschaftliche Kodex stellt Herausforderungen vor, die die Standard-Einheitentests nicht vollst\u00e4ndig angehen. Numerische Algorithmen konvergieren oder konvergieren nicht. Simulationen h\u00e4ngen von externen L\u00f6sern und I\/O-Systemen ab. Flie\u00dfkommaarithmetik kann sich \u00fcber Plattformen hinweg unterschiedlich verhalten.<\/p>\n<p>Randf\u00e4lle, die in normalen Daten niemals auftreten, wie z.<\/p>\n<p>Dieser Artikel behandelt Testmuster f\u00fcr wissenschaftlichen Python-Code \u00fcber die Grundlagen hinaus.<\/p>\n<h2>Die Testpyramide f\u00fcr wissenschaftlichen Code<\/h2>\n<p>Jede Teststrategie hat einen Platz auf der Pyramide. F\u00fcr den wissenschaftlichen Code sieht die Struktur wie folgt aus:<\/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>Unit-Tests bilden die Grundlage. \u00dcber ihnen befinden sich eigenschaftsbasierte Tests, Integrationstests und Simulationstests auf Systemebene. Diese h\u00f6heren Schichten machen das Testen f\u00fcr echte wissenschaftliche Arbeitsabl\u00e4ufe n\u00fctzlich.<\/p>\n<h2>Eigenschaftenbasiertes Testen mit Hypothese<\/h2>\n<h3>Warum eigenschaftsbasierte Tests f\u00fcr wissenschaftlichen Code wichtig sind<\/h3>\n<p>Hypothese ist nicht nur ein Lehrmittel. Es wird von gro\u00dfen wissenschaftlichen Python-Bibliotheken verwendet, um Fehler in numerischen Kernroutinen zu finden.<\/p>\n<p>Eigenschaftenbasierte Tests ver\u00e4ndern das mentale Modell des Tests. Anstatt Tests mit einer bestimmten Eingabe und einer erwarteten Ausgabe zu schreiben, schreiben Sie Tests, die die mathematischen Eigenschaften \u00fcberpr\u00fcfen. Die Hypothese generiert dann automatisch viele Eingaben, einschlie\u00dflich Kantenf\u00e4llen, die Sie m\u00f6glicherweise nicht manuell testen m\u00f6chten.<\/p>\n<h3>Konvertieren eines handgeschriebenen Tests in eigenschaftsbasierte<\/h3>\n<p>Betrachten Sie diesen Test f\u00fcr einen numerischen Solver-Wrapper:<\/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>Dieser Test \u00fcberpr\u00fcft eine Maschengr\u00f6\u00dfe und einen Zeitschritt. Es kann sogar passieren, wenn der Solver f\u00fcr gr\u00f6\u00dfere Netze oder unterschiedliche Zeitschrittwerte ausf\u00e4llt.<\/p>\n<p>Hier eine Hypothesenversion:<\/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>Dies f\u00e4ngt Probleme auf, die ein handgeschriebener Test \u00fcbersehen kann:<\/p>\n<ul>\n<li>Eckf\u00e4lle in der Netzgenerierung, einschlie\u00dflich d\u00fcnner Dom\u00e4nen und extremer Seitenverh\u00e4ltnisse.<\/li>\n<li>Zeitschrittsensitivit\u00e4t, insbesondere wenn gro\u00dfe <code>dt<\/code> -Werte explizite Schemata destabilisieren.<\/li>\n<li>Gleitkomma-Kantenf\u00e4lle, einschlie\u00dflich Rundungsgrenzen und ungew\u00f6hnliche numerische Bereiche.<\/li>\n<\/ul>\n<h3>Die Eigenschafts-Checkliste f\u00fcr wissenschaftliche Funktionen<\/h3>\n<p>Identifizieren Sie vor dem Schreiben eines Eigenschaftstests, welche Eigenschaften die Funktion erf\u00fcllen soll.<\/p>\n<table class=\"custom-table\">\n<tbody><tr>\n<th>Eigenschaftstyp<\/th>\n<th>Beispiel<\/th>\n<th>Warum es wichtig ist<\/th>\n<\/tr>\n<tr>\n<td>Erhaltung<\/td>\n<td>Masse, Energie oder Impuls bleiben erhalten<\/td>\n<td>Verhindert numerische Drift<\/td>\n<\/tr>\n<tr>\n<td>Symmetrie<\/td>\n<td><code>f(a, b) == f(b, a)<\/code> f\u00fcr symmetrische Operatoren<\/td>\n<td>F\u00e4ngt asymmetrische Implementierungsfehler ab<\/td>\n<\/tr>\n<tr>\n<td>Skalieren<\/td>\n<td><code>f(k*a, k*b) == k^n * f(a, b)<\/code> f\u00fcr homogene Funktionen<\/td>\n<td>Tests die dimensionale Konsistenz<\/td>\n<\/tr>\n<tr>\n<td>Monotonie<\/td>\n<td>Die Temperatur sinkt mit der Entfernung in einem einfachen Hitzeproblem<\/td>\n<td>\u00dcberpr\u00fcft die k\u00f6rperliche Korrektheit<\/td>\n<\/tr>\n<tr>\n<td>Grenzen<\/td>\n<td>Werte bleiben in einem g\u00fcltigen Bereich<\/td>\n<td>Verhindert nicht-physische Ergebnisse<\/td>\n<\/tr>\n<tr>\n<td>Null-Verhalten<\/td>\n<td><code>f(0)<\/code> Gibt den erwarteten Flanken-Case-Wert zur\u00fcck<\/td>\n<td>Schutz gegen Division durch null und einzelne F\u00e4lle<\/td>\n<\/tr>\n<\/tbody><\/table>\n<h2>Numerische Konvergenzpr\u00fcfung<\/h2>\n<h3>Warum Konvergenztests anders ist<\/h3>\n<p>Einheitentests pr\u00fcfen, ob Code eine bestimmte Antwort erzeugt. Konvergenztests pr\u00fcfen, ob der Code bei der Verfeinerung der Parameter der richtigen Antwort n\u00e4hert.<\/p>\n<p>Dies ist entscheidend f\u00fcr iterative L\u00f6ser, Zeitschrittschemata und Netzverfeinerungsstudien. Standard-Behauptungen wie <code>assert result == expected<\/code> erfassen kein Konvergenzverhalten. Sie m\u00fcssen Konvergenzmuster testen.<\/p>\n<h3>Testen der Solver-Konvergenz<\/h3>\n<p>Verschiedene Solver-Suiten verwenden unterschiedliche Konvergenzkriterien. Der entscheidende Punkt ist, dass die Konvergenzkriterien solverspezifisch sind und die Standardtoleranz m\u00f6glicherweise nicht f\u00fcr jedes Problem geeignet ist.<\/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>Toleranzstrategien<\/h3>\n<p>Tests sollten die g\u00e4ngigsten Toleranzkriterien abdecken, die vom Solver-Stack verwendet werden.<\/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>Divergenzf\u00e4lle testen<\/h3>\n<p>Sie sollten auch testen, dass der Code die Abweichung ordnungsgem\u00e4\u00df behandelt.<\/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>Integrationstests f\u00fcr Simulationsworkflows<\/h2>\n<h3>Warum Integrationstests wichtig sind<\/h3>\n<p>Unit-Tests \u00fcberpr\u00fcfen einzelne Funktionen. Integrationstests stellen sicher, dass die Funktionen korrekt zusammenarbeiten.<\/p>\n<p>F\u00fcr den wissenschaftlichen Code bedeutet dies das Testen von mehrstufigen Simulationen, Datenpipelines, Solver-Kopplungsmustern und E \/ A-Workflows.<\/p>\n<h3>Muster 1: Verspotten von externen Abh\u00e4ngigkeiten im wissenschaftlichen Kodex<\/h3>\n<p>Beim Testen von Funktionen, die externe APIs, Datenbanken oder Dateisysteme aufrufen, verwenden Sie Spott, um die numerische Logik zu isolieren.<\/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>Dies isoliert die Datenaufbereitungslogik aus der HDF5-Datei Write. Der Test \u00fcberpr\u00fcft, ob Ihre Code-Formate und -pakete korrekt sind, ohne auf E \/ A zu warten.<\/p>\n<h3>Muster 2: Parametrisierte Fixtures f\u00fcr Simulations-Sweeps<\/h3>\n<p>Anstatt einige Netzgr\u00f6\u00dfen manuell zu testen, verwenden Sie parametrisierte Vorrichtungen f\u00fcr systematische Sweeps.<\/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>Muster 3: Testen von mehrstufigen Workflows<\/h3>\n<p>Wissenschaftliche Simulationen verf\u00fcgen h\u00e4ufig \u00fcber mehrstufige Pipelines: Generierung, L\u00f6sung, Nachbearbeitung und Schreibausgabe.<\/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>Fortgeschrittenes Verspotten f\u00fcr wissenschaftliche Bibliothek<\/h2>\n<h3>Herausforderung<\/h3>\n<p>Das Testen von wissenschaftlichem Code kann langsam sein, da numpy-, scipy- und fipy-Funktionen komplexe R\u00fcckgabetypen und teure Ausf\u00fchrungspfade haben k\u00f6nnen. Wenn Sie sie verspotten, k\u00f6nnen Sie die Wrapper-Logik schnell testen.<\/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>Dadurch wird die Wrapper-Logik \u00fcberpr\u00fcft, ohne dass der tats\u00e4chliche Solver ausgef\u00fchrt wird. Der Test kann in Millisekunden statt Sekunden beendet werden.<\/p>\n<h3>Testen von Hin- und R\u00fcckfahrten f\u00fcr wissenschaftliche Daten<\/h3>\n<p>Das Testen, dass wissenschaftliche Daten einen Lese-Schreib-Lese-Zyklus \u00fcberstehen, ist ein leistungsstarkes Muster.<\/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>Dadurch werden Datenserialisierungsfehler, DType-Fehlanpassungen und HDF5-Chunking-Probleme erfasst, die die Ergebnisse stillschweigend besch\u00e4digen k\u00f6nnten.<\/p>\n<h2>Reproduzierbarkeit testen<\/h2>\n<h3>Deterministisches Saatgutmanagement<\/h3>\n<p>Gleitkommaoperationen k\u00f6nnen deterministisch sein, wenn der Seed festgelegt ist, aber die Reproduzierbarkeit erfordert auch konsistente Bibliotheksversionen, Kompilierungsflags und MPI-Dekompositionsmuster.<\/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>Plattform\u00fcbergreifende Gleitkomma-Tests<\/h3>\n<p>Verschiedene Plattformen k\u00f6nnen aufgrund von Hardware- und Bibliotheksunterschieden leicht unterschiedliche Gleitkommaergebnisse erzeugen.<\/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>Was wir empfehlen: Die Teststrategie<\/h2>\n<p>Verwenden Sie f\u00fcr ein produktionswissenschaftliches Python-Projekt eine geschichtete Teststrategie.<\/p>\n<h3>Haben m\u00fcssen<\/h3>\n<ol>\n<li>Unit-Tests mit <code>pytest.approx<\/code> f\u00fcr numerische Behauptungen.<\/li>\n<li>Parametrisierte Ger\u00e4te f\u00fcr systematische Tests \u00fcber Parameter hinweg.<\/li>\n<li>Konvergenztests, die das Solververhalten w\u00e4hrend der Verfeinerung \u00fcberpr\u00fcfen.<\/li>\n<\/ol>\n<h3>Sollte haben<\/h3>\n<ol start=\"4\">\n<li>Hypotheseneigenschaftsbasierte Tests f\u00fcr Randf\u00e4lle und numerische Robustheit.<\/li>\n<li>Integrationstests f\u00fcr mehrstufige Simulationsworkflows.<\/li>\n<li>Mocking-Strategien f\u00fcr externe Abh\u00e4ngigkeiten und Solver-Schnittstellen.<\/li>\n<\/ol>\n<h3>Sch\u00f6n zu haben<\/h3>\n<ol start=\"7\">\n<li>Plattform\u00fcbergreifende Reproduzierbarkeitstests.<\/li>\n<li>Daten-Rundtrip-Tests, die wissenschaftliche Daten schreiben, lesen und \u00fcberpr\u00fcfen.<\/li>\n<li>Leistungsbenchmarks mit <code>pytest-benchmark<\/code>.<\/li>\n<\/ol>\n<h2>H\u00e4ufige Fehler<\/h2>\n<h3>Fehler 1: Toleranzabh\u00e4ngige Aussagen falsch verwenden<\/h3>\n<p>Schreiben Sie keine numerischen Tests wie <code>assert result == expected<\/code>. Verwenden Sie <code>pytest.approx<\/code> oder toleranzbewusste Zusicherungen f\u00fcr numerischen Code. Die Toleranz h\u00e4ngt von der Problemskala ab, so dass eine feste Toleranz bei gr\u00f6\u00dferen Problemen fehlschl\u00e4gt.<\/p>\n<h3>Fehler 2: Konvergenz ignorieren<\/h3>\n<p>Wenn der Code iterative Solver verwendet, \u00fcberspringen Sie keine Konvergenztests. Ein Solver, der auf einem Netz arbeitet, kann auf einem anderen divergieren. Testen Sie das Konvergenzverhalten, nicht nur das Endergebnis.<\/p>\n<h3>Fehler 3: Nur den gl\u00fccklichen Weg testen<\/h3>\n<p>Property-basierte Tests zwingen Sie dazu, Randf\u00e4lle zu testen, die manuelle Tests h\u00e4ufig \u00fcbersehen. F\u00fcgen Sie mindestens einen Hypothesentest f\u00fcr jede wichtige numerische Funktion hinzu. Es kann Fehler aufdecken, die normale beispielbasierte Tests niemals abfangen.<\/p>\n<h2>Zusammenfassung<\/h2>\n<p>Unit-Tests sind notwendig, aber sie reichen f\u00fcr den wissenschaftlichen Kodex nicht aus. Property-basierte Tests, Konvergenztests, Integrationsworkflows und Reproduzierbarkeitspr\u00fcfungen befassen sich mit den spezifischen Herausforderungen, die numerische Simulationen mit sich bringen.<\/p>\n<p>Die wichtigste Erkenntnis ist, dass es bei wissenschaftlichen Tests nicht nur darum geht, zu \u00fcberpr\u00fcfen, ob Code eine richtige Antwort liefert. Es geht darum zu \u00fcberpr\u00fcfen, ob sich der Code richtig verh\u00e4lt. Es sollte elegant konvergieren, konservieren, skalieren und bearbeiten.<\/p>\n<p>Beginnen Sie mit den Must-Have-Mustern und f\u00fcgen Sie dann die Muster hinzu, wenn die Testsuite ausgereift ist. Verwenden Sie die sch\u00f6nen Muster, um die Codebasis im Laufe der Zeit zu h\u00e4rten.<\/p>\n<p>Property-basierte Tests mit Hypothese sind eine der hochwertigsten Erg\u00e4nzungen, die Sie vornehmen k\u00f6nnen, da sie Fehler auffangen, die manuelle Tests h\u00e4ufig \u00fcbersehen.<\/p>\n<h2>Verwandte Anleitungen<\/h2>\n<ul>\n<li><a href=\"https:\/\/matforge.org\/unit-testing-scientific-code-pytest-strategies-research-projects\/\"> Einheitentest f\u00fcr wissenschaftlichen Kodex: PYTest-Strategien f\u00fcr Forschungsprojekte <\/a> - Die Grundlage, die Sie vor diesem Artikel ben\u00f6tigen.<\/li>\n<li><a href=\"https:\/\/matforge.org\/when-to-use-fem-fvm-fdm\/\">Wann verwendet werden FEM, FVM oder FDM: Ein praktischer Vergleich f\u00fcr Anf\u00e4nger <\/a> - Kontext zu numerischen Methoden.<\/li>\n<li><a href=\"https:\/\/matforge.org\/mesh-quality-convergence-studies-guide-2\/\">Mesh-Qualit\u00e4ts- und Konvergenzstudien: Ein praktischer Leitfaden f\u00fcr wissenschaftliche Simulationen <\/a> - Verwandter Konvergenztestkontext.<\/li>\n<\/ul>\n<h2>Was macht man als n\u00e4chstes<\/h2>\n<p>Wenn Sie gerade angefangen haben, Tests f\u00fcr wissenschaftlichen Code zu schreiben, beginnen Sie mit den oben genannten Mustern. Sobald diese vorhanden sind, f\u00fcgen Sie Hypothesentests f\u00fcr die wichtigsten numerischen Funktionen hinzu, insbesondere Funktionen, die in Simulationen wiederholt aufgerufen werden.<\/p>\n<p>F\u00fcr Teams, die an produktiven wissenschaftlichen Software arbeiten, kann sich die Investition in objektbasierte Tests schnell auszahlen. Es handelt sich um dieselbe Strategie, die von gro\u00dfen wissenschaftlichen Python-Bibliotheken verwendet wird, und ist eine der effektivsten M\u00f6glichkeiten, um Edge-Case-Fehler im numerischen Code zu finden.<\/p>\n<p>Wenn Sie Hilfe beim Entwerfen einer Teststrategie f\u00fcr ein Simulationsprojekt ben\u00f6tigen, sollten Sie eine Beratung buchen. Eine praktische \u00dcberpr\u00fcfung kann Ihre Codestruktur auf Einheitentests, Konvergenztests, Integrationstests und Reproduzierbarkeitspr\u00fcfungen abbilden.<\/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\"> 7<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span>Schl\u00fcssel zum Mitnehmen Pytest-Grundlagen decken Einheitentest ab. Fixtures, Parametrisierung und pytest.approx sind Ihr Ausgangspunkt, nicht die Ziellinie. Eigenschaftenbasierte Tests mit Hypothese fangen Randf\u00e4lle ab, die manuelle Tests h\u00e4ufig \u00fcbersehen. Es wird von wichtigen wissenschaftlichen Python-Projekten wie Numpy, Jax und Pytorch verwendet. Numerische Konvergenztests erfordern Solver-spezifische Toleranzstrategien. Standard-Behauptungen erfassen kein Konvergenzverhalten. Integrationstests f\u00fcr Simulations-Workflows erfordern Spottmuster, [&hellip;]<\/p>\n","protected":false,"raw":""},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_locale":"de_DE","_original_post":"https:\/\/matforge.org\/?p=365","iawp_total_views":0,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-854","post","type-post","status-publish","format-standard","hentry","category-simulation-modeling-projects","de-DE"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Python-Testmuster f\u00fcr wissenschaftlichen Code<\/title>\n<meta name=\"description\" content=\"Erlernen Sie fortgeschrittene Python-Testmuster f\u00fcr wissenschaftlichen Code, einschlie\u00dflich Hypothese, Konvergenztests, Integrationstests, Mocking und Reproduzierbarkeitspr\u00fcfungen\" \/>\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\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Python-Testmuster f\u00fcr wissenschaftlichen Code\" \/>\n<meta property=\"og:description\" content=\"Erlernen Sie fortgeschrittene Python-Testmuster f\u00fcr wissenschaftlichen Code, einschlie\u00dflich Hypothese, Konvergenztests, Integrationstests, Mocking und Reproduzierbarkeitspr\u00fcfungen\" \/>\n<meta property=\"og:url\" content=\"https:\/\/matforge.org\/de\/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-07-30T12:22:24+00:00\" \/>\n<meta name=\"author\" content=\"Elena Markovska\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Elena Markovska\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"11\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/\"},\"author\":{\"name\":\"Elena Markovska\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/980162bb5de46742daece973661d93da\"},\"headline\":\"Python-Testmuster f\u00fcr wissenschaftlichen Code: Beyond PyTest Basics\",\"datePublished\":\"2026-07-30T12:22:24+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/\"},\"wordCount\":1429,\"commentCount\":0,\"articleSection\":[\"Simulation & amp; Modellierungsprojekte\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/matforge.org\\\/de\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/\",\"url\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/\",\"name\":\"Python-Testmuster f\u00fcr wissenschaftlichen Code\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/#website\"},\"datePublished\":\"2026-07-30T12:22:24+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/980162bb5de46742daece973661d93da\"},\"description\":\"Erlernen Sie fortgeschrittene Python-Testmuster f\u00fcr wissenschaftlichen Code, einschlie\u00dflich Hypothese, Konvergenztests, Integrationstests, Mocking und Reproduzierbarkeitspr\u00fcfungen\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/matforge.org\\\/de\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/matforge.org\\\/de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Python-Testmuster f\u00fcr wissenschaftlichen Code: 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\":\"de\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/980162bb5de46742daece973661d93da\",\"name\":\"Elena Markovska\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@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":"Python-Testmuster f\u00fcr wissenschaftlichen Code","description":"Erlernen Sie fortgeschrittene Python-Testmuster f\u00fcr wissenschaftlichen Code, einschlie\u00dflich Hypothese, Konvergenztests, Integrationstests, Mocking und Reproduzierbarkeitspr\u00fcfungen","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\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/","og_locale":"de_DE","og_type":"article","og_title":"Python-Testmuster f\u00fcr wissenschaftlichen Code","og_description":"Erlernen Sie fortgeschrittene Python-Testmuster f\u00fcr wissenschaftlichen Code, einschlie\u00dflich Hypothese, Konvergenztests, Integrationstests, Mocking und Reproduzierbarkeitspr\u00fcfungen","og_url":"https:\/\/matforge.org\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/","og_site_name":"matforge.org","article_published_time":"2026-07-30T12:22:24+00:00","author":"Elena Markovska","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Elena Markovska","Gesch\u00e4tzte Lesezeit":"11\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/matforge.org\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/#article","isPartOf":{"@id":"https:\/\/matforge.org\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/"},"author":{"name":"Elena Markovska","@id":"https:\/\/matforge.org\/#\/schema\/person\/980162bb5de46742daece973661d93da"},"headline":"Python-Testmuster f\u00fcr wissenschaftlichen Code: Beyond PyTest Basics","datePublished":"2026-07-30T12:22:24+00:00","mainEntityOfPage":{"@id":"https:\/\/matforge.org\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/"},"wordCount":1429,"commentCount":0,"articleSection":["Simulation & amp; Modellierungsprojekte"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/matforge.org\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/matforge.org\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/","url":"https:\/\/matforge.org\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/","name":"Python-Testmuster f\u00fcr wissenschaftlichen Code","isPartOf":{"@id":"https:\/\/matforge.org\/#website"},"datePublished":"2026-07-30T12:22:24+00:00","author":{"@id":"https:\/\/matforge.org\/#\/schema\/person\/980162bb5de46742daece973661d93da"},"description":"Erlernen Sie fortgeschrittene Python-Testmuster f\u00fcr wissenschaftlichen Code, einschlie\u00dflich Hypothese, Konvergenztests, Integrationstests, Mocking und Reproduzierbarkeitspr\u00fcfungen","breadcrumb":{"@id":"https:\/\/matforge.org\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/matforge.org\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/matforge.org\/de\/python-testing-patterns-for-scientific-code-beyond-pytest-basics\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/matforge.org\/de\/"},{"@type":"ListItem","position":2,"name":"Python-Testmuster f\u00fcr wissenschaftlichen Code: 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":"de"},{"@type":"Person","@id":"https:\/\/matforge.org\/#\/schema\/person\/980162bb5de46742daece973661d93da","name":"Elena Markovska","image":{"@type":"ImageObject","inLanguage":"de","@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\/854","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=854"}],"version-history":[{"count":1,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/854\/revisions"}],"predecessor-version":[{"id":954,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/854\/revisions\/954"}],"wp:attachment":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/media?parent=854"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/categories?post=854"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/tags?post=854"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}