{"id":849,"date":"2026-07-30T12:22:25","date_gmt":"2026-07-30T12:22:25","guid":{"rendered":"https:\/\/matforge.org\/?p=849","raw":"https:\/\/matforge.org\/?p=849"},"modified":"2026-07-30T12:22:25","modified_gmt":"2026-07-30T12:22:25","slug":"unit-testing-scientific-code-pytest-strategies-research-projects","status":"publish","type":"post","link":"https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/","title":{"rendered":"Unit-Testing f\u00fcr wissenschaftlichen Code: PYTest-Strategien f\u00fcr Forschungsprojekte","raw":"Unit-Testing f\u00fcr wissenschaftlichen Code: PYTest-Strategien f\u00fcr Forschungsprojekte"},"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\"> 10<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span><p>Unit-Tests sind f\u00fcr vertrauensw\u00fcrdige wissenschaftliche Software nicht verhandelbar. Im Gegensatz zu kommerziellen Anwendungen fehlen dem Forschungscode h\u00e4ufig formelle Tests, was zu nicht reproduzierbaren Ergebnissen und vergeudeten Anstrengungen f\u00fchrt. Dieser Leitfaden behandelt Pytest-Strategien speziell f\u00fcr wissenschaftliche Python-Projekte: Umgang mit numerischer Pr\u00e4zision mit <code>pytest.approx<\/code>, Isolierung externer Abh\u00e4ngigkeiten mit Spott, Verwendung von Fixtures und Parametrisierung effizient und Integration von Tests in kontinuierliche Integrations-Pipelines. Sie erfahren, wann die Black-Box-Validierung gegen ver\u00f6ffentlichte Ergebnisse verwendet werden muss und wie Sie Tests entwerfen, die die Code-Entwicklung \u00fcberleben, ohne br\u00fcchig zu werden.<\/p>\n<h2>Warum Unit-Testing in der Forschungssoftware wichtig ist<\/h2>\n<p>Wissenschaftliche Software existiert in einem anspruchsvollen Raum. Es muss flexibel genug sein, um neue Hypothesen zu untersuchen, aber zuverl\u00e4ssig genug, dass ver\u00f6ffentlichte Ergebnisse Monate oder Jahre sp\u00e4ter reproduziert werden k\u00f6nnen. Im Gegensatz zu kommerzieller Software mit klaren Spezifikationen entwickelt sich h\u00e4ufig neben Experimenten auch der Forschungscode, wobei sich die Anforderungen \u00e4ndern, wenn sich neue Entdeckungen ergeben.<\/p>\n<p>Die Folgen unzureichender Untersuchungen in der Forschung sind schwerwiegend:<\/p>\n<ul>\n<li><strong>Fehlproduzierbare Ergebnisse<\/strong>: Verschiedene Forscher erhalten unterschiedliche Ausgaben aus demselben Code<\/li>\n<li><strong>Silent Bugs<\/strong>: numerische Fehler, die individuell zu signifikanten Ungenauigkeiten erscheinen<\/li>\n<li><strong>Wissensverlust<\/strong>: Wenn urspr\u00fcngliche Entwickler gehen, dienen Tests als ausf\u00fchrbare Dokumentation<\/li>\n<li><strong>Vergeudete Anstrengung<\/strong>: Debuggen wird zu einer Detektivjagd anstelle eines systematischen Prozesses<\/li>\n<\/ul>\n<p>Unit-Tests l\u00f6sen diese Probleme durch die Validierung kleiner, isolierter Komponenten Ihres Codes. Jeder Test \u00fcberpr\u00fcft, ob sich eine bestimmte Funktion oder Klasse wie erwartet bei definierten Eingaben verh\u00e4lt. Wenn Tests konsistent \u00fcber Umgebungen hinweg bestehen, haben Sie den Nachweis, dass Ihr Code zuverl\u00e4ssige Ergebnisse liefert.<\/p>\n<p>Das Testen des wissenschaftlichen Codes stellt jedoch einzigartige Herausforderungen dar, die Standard-Software-Testans\u00e4tze nicht vollst\u00e4ndig angehen.<\/p>\n<h2>Einzigartige Herausforderungen beim Testen von wissenschaftlichem Code<\/h2>\n<p>Der wissenschaftliche und numerische Code unterscheidet sich von typischen Gesch\u00e4ftsanwendungen in verschiedenen Bereichen, die sich auf die Teststrategie auswirken.<\/p>\n<h3>Numerische Pr\u00e4zision und Gleitkommafehler<\/h3>\n<p>Flie\u00dfkommaarithmetik ist von Natur aus ungenau. Da Computer Dezimalzahlen darstellen, ist <code>0.1 + 0.2<\/code> nicht genau <code>0.3<\/code> in der bin\u00e4ren Gleitkommadarstellung. In wissenschaftlichen Simulationen, die Tausende oder Millionen von Operationen beinhalten, sammeln sich diese winzigen Fehler an.<\/p>\n<p>Ein naiver Test, der exakte Gleichheit verwendet (<code>==<\/code>) wird zeitweise oder auf unterschiedlicher Hardware fehlschlagen:<\/p>\n<pre><code class=\"language-python\">def test_numerical_computation():\n    result = complex_simulation()  # returns 1.0000000000000002\n    assert result == 1.0  # FAILS! Even though the difference is negligible\n<\/code><\/pre>\n<p>Die L\u00f6sung besteht darin, toleranzbasierte Vergleiche zu verwenden. PYTEST stellt hierf\u00fcr <code>pytest.approx()<\/code> zur Verf\u00fcgung:<\/p>\n<pre><code class=\"language-python\">def test_numerical_computation():\n    result = complex_simulation()\n    assert result == pytest.approx(1.0, rel=1e-9, abs=1e-12)\n<\/code><\/pre>\n<p><code>rel<\/code> (relative Toleranz) ist n\u00fctzlich f\u00fcr Werte jeglicher Gr\u00f6\u00dfe; <code>abs<\/code> (Absolute Toleranz) Behandelt F\u00e4lle, in denen der erwartete Wert nahe Null liegt. Die Standardeinstellungen sind <code>rel=1e-6<\/code> und <code>abs=1e-12<\/code>, aber der wissenschaftliche Code erfordert h\u00e4ufig engere Toleranzen.<\/p>\n<h3>Unbekannte &#8222;richtige&#8220; Antworten<\/h3>\n<p>In vielen Forschungsszenarien haben Sie keine bekannte korrekte Ausgabe. Die Simulation k\u00f6nnte Neuland erforschen. Wie testen Sie Code, wenn Sie nicht wissen, wie die Antwort lauten soll?<\/p>\n<p>Mehrere Strategien funktionieren:<\/p>\n<ol>\n<li><strong>Inverse Operations<\/strong>: Wenn Ihr Code <code>B = f(A)<\/code> berechnet, testen Sie auch <code>A \u2248 f\u207b\u00b9(B)<\/code>.<\/li>\n<li><strong>Erhaltungsgesetze<\/strong>: Vergewissern Sie sich, dass Masse, Energie oder Impuls innerhalb der Toleranz erhalten bleiben.<\/li>\n<li><strong>Begrenzende F\u00e4lle<\/strong>: Testverhalten in vereinfachten Grenzen, wenn analytische L\u00f6sungen existieren.<\/li>\n<li><strong>Regressionstests<\/strong>: Speichern Sie die Ausgaben eines vertrauensw\u00fcrdigen Laufs und erkennen Sie unerwartete \u00c4nderungen.<\/li>\n<\/ol>\n<h3>Externe Abh\u00e4ngigkeiten und starke Berechnung<\/h3>\n<p>Wissenschaftlicher Code h\u00e4ngt oft von:<\/p>\n<ul>\n<li>Gro\u00dfe Datens\u00e4tze (Terabyte der Simulationseingabe)<\/li>\n<li>Externe Solver oder Bibliotheken (HPC-Pakete, Fortran-Bibliotheken)<\/li>\n<li>Datei-E \/ A mit komplexen Formaten<\/li>\n<li>Datenbankverbindungen oder APIs<\/li>\n<\/ul>\n<p>Das Ausf\u00fchren des vollen Systems in jedem Unit-Test ist unpraktisch. Sie brauchen Isolation.<\/p>\n<h2>PYTest-Funktionen, die Probleme beim Testen von Forschungstests l\u00f6sen<\/h2>\n<p>Pytest bietet mehrere Funktionen, die f\u00fcr den wissenschaftlichen Code besonders wertvoll sind.<\/p>\n<h3>Fixtures f\u00fcr Setup und Teardown<\/h3>\n<p>Fixtures kapseln den Setup-Code, der vor Tests ausgef\u00fchrt wird. F\u00fcr wissenschaftliche Tests k\u00f6nnen Fixtures:<\/p>\n<ul>\n<li>Erstellen Sie tempor\u00e4re Testdaten oder Netze<\/li>\n<li>Initialisieren Sie Simulationsobjekte mit bekannten Parametern<\/li>\n<li>Beseitigen Sie tempor\u00e4re Dateien nach den Tests<\/li>\n<li>Stellen Sie wiederverwendbare Testkonfigurationen bereit<\/li>\n<\/ul>\n<pre><code class=\"language-python\">import pytest\nimport tempfile\nimport numpy as np\n\n@pytest.fixture\ndef simple_mesh():\n    \"\"\"Create a small 1D mesh for testing.\"\"\"\n    from fipy import Grid1D\n    return Grid1D(dx=0.1, nx=10)\n\n@pytest.fixture\ndef diffusion_solver(simple_mesh):\n    \"\"\"Set up a diffusion solver on the test mesh.\"\"\"\n    from fipy import CellVariable, DiffusionTerm\n    var = CellVariable(name=\"concentration\", mesh=simple_mesh, value=1.0)\n    eq = DiffusionTerm(coeff=1.0) == 0\n    return var, eq\n<\/code><\/pre>\n<h3>Parametrierung f\u00fcr mehrere Szenarien<\/h3>\n<p>Anstatt f\u00fcr \u00e4hnliche F\u00e4lle separate Testfunktionen zu schreiben, verwenden Sie <code>@pytest.mark.parametrize<\/code>, um dieselbe Testlogik mit unterschiedlichen Eing\u00e4ngen auszuf\u00fchren.<\/p>\n<pre><code class=\"language-python\">@pytest.mark.parametrize(\"dx,nx,expected_volume\", [\n    (0.1, 10, 1.0),\n    (0.01, 100, 1.0),\n    (0.001, 1000, 1.0),\n])\ndef test_mesh_volume(dx, nx, expected_volume):\n    \"\"\"Test that mesh volume matches domain size.\"\"\"\n    mesh = Grid1D(dx=dx, nx=nx)\n    assert mesh.cellVolumes.sum() == pytest.approx(expected_volume)\n<\/code><\/pre>\n<p>Parametrierung ist besonders n\u00fctzlich f\u00fcr:<\/p>\n<ul>\n<li>Testen von Kanten (Nullwerte, sehr kleine\/gro\u00dfe Zahlen)<\/li>\n<li>\u00dcberpr\u00fcfen des Verhaltens \u00fcber verschiedene Netzaufl\u00f6sungen<\/li>\n<li>Validierung mehrerer Randbedingungstypen<\/li>\n<li>\u00dcberpr\u00fcfen verschiedener Werte f\u00fcr Materialeigenschaften<\/li>\n<\/ul>\n<p>F\u00fcr Research Code k\u00f6nnen Sie gegen bekannte Benchmark-Ergebnisse aus ver\u00f6ffentlichten Artikeln parametrisieren.<\/p>\n<h3>Verspotten von externen Abh\u00e4ngigkeiten<\/h3>\n<p>Das Verspotten ersetzt echte Abh\u00e4ngigkeiten durch kontrollierte F\u00e4lschungen. Dies isoliert das zu testende Ger\u00e4t und macht Tests schneller und zuverl\u00e4ssiger.<\/p>\n<p><strong>Wann im wissenschaftlichen Code verspotten:<\/strong><\/p>\n<ul>\n<li>Externe Datendateien: Ersetzen Sie gro\u00dfe Datens\u00e4tze durch minimale synthetische Daten, die dieselben Codepfade aus\u00fcben<\/li>\n<li>HPC-Solver: Mock teure FORTRAN-Bibliotheken mit reinen Python-Implementierungen, die bekannte Ergebnisse zur\u00fcckgeben<\/li>\n<li>Netzwerk-APIs: Stub-Remote-Dienste, die Parameter oder Konfiguration bereitstellen<\/li>\n<li>Zufallszahlengeneratoren: Samen Sie sie, um deterministische Sequenzen zu erzeugen<\/li>\n<\/ul>\n<pre><code class=\"language-python\">from unittest.mock import patch, MagicMock\n\ndef test_simulation_with_external_data():\n    # Mock the data loading function to return small, known data\n    with patch('mycode.load_large_dataset') as mock_load:\n        mock_load.return_value = np.array([1.0, 2.0, 3.0])\n        result = run_simulation()\n        assert result.converged\n<\/code><\/pre>\n<p><strong>Wichtige Richtlinie<\/strong>: Verspotten Sie die Abh\u00e4ngigkeiten Ihres eigenen Codes, nicht die Bibliotheken von Drittanbietern, die Sie nicht kontrollieren. Folgen Sie dem Prinzip &#8222;Verspotten Sie nicht, was Sie nicht besitzen.&#8220;<\/p>\n<h3>Verwenden von <code>pytest.approx<\/code> f\u00fcr numerische Vergleiche<\/h3>\n<p>Gleitkommavergleiche m\u00fcssen Rundungsfehler ber\u00fccksichtigen. Das Objekt von Pytest <code>approx<\/code> behandelt dies elegant:<\/p>\n<pre><code class=\"language-python\">def test_diffusion_solution():\n    \"\"\"Test that diffusion reaches expected steady state.\"\"\"\n    concentration = solve_diffusion(time=100.0)\n    expected = 0.5  # analytical steady state for this boundary condition\n    assert concentration.mean() == pytest.approx(expected, rel=1e-6)\n<\/code><\/pre>\n<p>Sie k\u00f6nnen auch <code>approx<\/code> mit Arrays verwenden:<\/p>\n<pre><code class=\"language-python\">def test_array_computation():\n    result = compute_field()\n    expected = np.array([1.0, 2.0, 3.0])\n    assert result == pytest.approx(expected)\n<\/code><\/pre>\n<p>W\u00e4hlen Sie f\u00fcr den wissenschaftlichen Code Toleranzen basierend auf:<\/p>\n<ul>\n<li>Die numerische Genauigkeit Ihrer Methoden (z.<\/li>\n<li>Die von Ihrer Anwendung erforderliche Pr\u00e4zision (Engineering Toleranz vs. Explorationsforschung)<\/li>\n<\/ul>\n<h2>Testgetriebene Entwicklung f\u00fcr Forschungsprojekte<\/h2>\n<p>Test-Driven Development (TDD) folgt einem einfachen Zyklus: Schreiben Sie einen fehlgeschlagenen Test, schreiben Sie dann minimalen Code, um ihn zu bestehen, und dann \u00fcberarbeiten Sie ihn. W\u00e4hrend TDD in kommerzieller Software gut etabliert ist, widersetzen sich Forschungsprojekte h\u00e4ufig aufgrund wahrgenommener Zeitbeschr\u00e4nkungen.<\/p>\n<p>Die Realit\u00e4t: TDD spart Zeit in der Forschung, indem sie Fehler abfangen, bevor sie sich durch Experimente ausbreiten. Das Schreiben von Tests zwingt Sie dazu, die Schnittstelle und das erwartete Verhalten jeder Funktion vor der Implementierung zu kl\u00e4ren.<\/p>\n<p><strong>TDD f\u00fcr die wissenschaftliche Erforschung angepasst:<\/strong><\/p>\n<ol>\n<li>Beginnen Sie mit einem einfachen Modell oder Algorithmus, das Sie analytisch verstehen<\/li>\n<li>Schreiben Sie Tests, die gegen bekannte Ergebnisse validieren (Analytische L\u00f6sungen, Begrenzungsf\u00e4lle)<\/li>\n<li>Implementieren Sie den Code, um diese Tests zu bestehen<\/li>\n<li>Erweitern Sie das Modell inkrementell und f\u00fcgen Sie Tests f\u00fcr jede neue Funktion hinzu<\/li>\n<li>Wenn Sie einen Fehler entdecken, schreiben Sie einen Test, der ihn zuerst reproduziert, und beheben Sie ihn dann<\/li>\n<\/ol>\n<p>TDD funktioniert gut f\u00fcr:<\/p>\n<ul>\n<li>Utility-Funktionen (Mesh-Generierung, Koordinatentransformationen)<\/li>\n<li>Mathematische Operationen (Matrix-Manipulationen, Sonderfunktionen)<\/li>\n<li>Datenverarbeitungs-Pipelines (Parsing, Filtern, Normalisierung)<\/li>\n<li>Konfigurationsvalidierung<\/li>\n<\/ul>\n<p>TDD ist weniger geeignet f\u00fcr:<\/p>\n<ul>\n<li>Sehr exploratorischer Code, bei dem die Schnittstelle selbst unsicher ist<\/li>\n<li>Einmalige Skripte, die nicht wiederverwendet werden<\/li>\n<li>Code, der von noch nicht verf\u00fcgbaren externen Ressourcen abh\u00e4ngt<\/li>\n<\/ul>\n<p>In der Praxis funktioniert ein hybrider Ansatz am besten: Schreibtests f\u00fcr stabile, grundlegende Komponenten; Verwenden Sie leichtere Integrationstests f\u00fcr experimentelle Abschnitte.<\/p>\n<h2>Organisation von Tests f\u00fcr wissenschaftliche Projekte<\/h2>\n<p>Wo sollen Testdateien leben? Pytest bietet Flexibilit\u00e4t:<\/p>\n<pre><code>my_research_project\/\n\u251c\u2500\u2500 src\/\n\u2502   \u2514\u2500\u2500 mypackage\/\n\u2502       \u251c\u2500\u2500 __init__.py\n\u2502       \u251c\u2500\u2500 solver.py\n\u2502       \u2514\u2500\u2500 mesh.py\n\u251c\u2500\u2500 tests\/\n\u2502   \u251c\u2500\u2500 __init__.py\n\u2502   \u251c\u2500\u2500 test_solver.py\n\u2502   \u251c\u2500\u2500 test_mesh.py\n\u2502   \u2514\u2500\u2500 conftest.py  # shared fixtures\n\u251c\u2500\u2500 data\/\n\u2502   \u2514\u2500\u2500 reference_results\/  # stored outputs for regression tests\n\u251c\u2500\u2500 .github\/\n\u2502   \u2514\u2500\u2500 workflows\/\n\u2502       \u2514\u2500\u2500 ci.yml  # GitHub Actions CI configuration\n\u251c\u2500\u2500 pyproject.toml\n\u2514\u2500\u2500 README.md\n<\/code><\/pre>\n<p><strong>Schl\u00fcsselkonventionen:<\/strong><\/p>\n<ul>\n<li>Testen Sie die Tests in einem separaten Verzeichnis <code>tests\/<\/code> parallel zu <code>src\/<\/code> (oder <code>lib\/<\/code>)<\/li>\n<li>Name Testdateien <code>test_*.py<\/code> oder <code>*_test.py<\/code><\/li>\n<li>Name Testfunktionen <code>test_*()<\/code>, um PyTest automatisch zu erkennen<\/li>\n<li>Verwenden Sie <code>conftest.py<\/code> f\u00fcr Ger\u00e4te, die \u00fcber mehrere Testdateien geteilt werden<\/li>\n<\/ul>\n<p>Bei FIPY-basierten Projekten stimmen Strukturtests der Modulhierarchie \u00fcberein:<\/p>\n<pre><code>fipy_project\/\n\u251c\u2500\u2500 fipy\/\n\u2502   \u251c\u2500\u2500 meshes\/\n\u2502   \u2502   \u2514\u2500\u2500 grid1d.py\n\u2502   \u2514\u2500\u2500 terms\/\n\u2502       \u2514\u2500\u2500 diffusion.py\n\u251c\u2500\u2500 tests\/\n\u2502   \u251c\u2500\u2500 meshes\/\n\u2502   \u2502   \u2514\u2500\u2500 test_grid1d.py\n\u2502   \u2514\u2500\u2500 terms\/\n\u2502       \u2514\u2500\u2500 test_diffusion.py\n<\/code><\/pre>\n<h2>Integration mit kontinuierlicher Integration<\/h2>\n<p>Unit-Tests liefern nur einen Wert, wenn sie konsistent ausgef\u00fchrt werden. Continuous Integration (CI) automatisiert die Testausf\u00fchrung, wenn sich der Code \u00e4ndert.<\/p>\n<p>GitHub Actions bietet ein einfaches CI-Setup f\u00fcr Python-Projekte:<\/p>\n<pre><code class=\"language-yaml\"># .github\/workflows\/ci.yml\nname: CI\n\non: [push, pull_request]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [\"3.9\", \"3.10\", \"3.11\"]\n\n    steps:\n    - uses: actions\/checkout@v3\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions\/setup-python@v4\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install -e .[test]\n    - name: Run tests with pytest\n      run: |\n        pytest --cov=src --cov-report=xml --cov-report=html\n    - name: Upload coverage\n      uses: codecov\/codecov-action@v3\n<\/code><\/pre>\n<p>CI stellt sicher:<\/p>\n<ul>\n<li>Tests bestehen auf mehreren Plattformen (Linux, macOS, Windows)<\/li>\n<li>Tests \u00fcbergeben mehrere Python-Versionen<\/li>\n<li>Die Codeabdeckung wird im Laufe der Zeit verfolgt<\/li>\n<li>Pull-Anforderungen werden vor dem Zusammenf\u00fchren validiert<\/li>\n<\/ul>\n<p>F\u00fcr Forschungssoftware sollten Sie Folgendes hinzuf\u00fcgen:<\/p>\n<ul>\n<li>Tests, die mit verschiedenen Versionen von Schl\u00fcsselabh\u00e4ngigkeiten ausgef\u00fchrt werden (numpy, scipy, fipy)<\/li>\n<li>Leistungsregressionspr\u00fcfungen (stellen Sie sicher, dass die Algorithmen nicht langsamer werden)<\/li>\n<li>Dokumentations-Builds zum \u00dcberpr\u00fcfen von Beispielen funktionieren immer noch<\/li>\n<\/ul>\n<h2>h\u00e4ufige Fehler und wie man sie vermeidet<\/h2>\n<p>Basierend auf den Best Practices der Forschung und der Branche finden Sie hier die h\u00e4ufigsten Fehler im wissenschaftlichen Code:<\/p>\n<h3>1. Testen von Implementierungsdetails anstelle von Verhalten<\/h3>\n<p>Das Testen der internen Implementierung macht Tests spr\u00f6de. Wenn Sie den Code \u00fcberarbeiten, sollten die Tests weiterhin bestehen, wenn das externe Verhalten korrekt ist.<\/p>\n<pre><code class=\"language-python\"># \u274c Bad: tests internal state\ndef test_algorithm_updates_counter():\n    obj = MyAlgorithm()\n    obj.step()\n    assert obj.counter == 1  # fragile if counter implementation changes\n\n# \u2705 Better: tests observable outcome\ndef test_algorithm_produces_correct_result():\n    obj = MyAlgorithm()\n    result = obj.run()\n    assert result == expected\n<\/code><\/pre>\n<h3>2. Numerische Toleranz ignorieren<\/h3>\n<p>Exakte Vergleiche bei Gleitkomma-Ergebnissen verursachen flockige Tests, die zuf\u00e4llig oder auf unterschiedlicher Hardware fehlschlagen. Verwenden Sie f\u00fcr numerische Ausgaben immer <code>pytest.approx()<\/code> oder \u00e4hnliche toleranzbasierte Zusicherungen.<\/p>\n<h3>3. Schreiben Sie langsame Tests<\/h3>\n<p>Unit-Tests sollten schnell ausgef\u00fchrt werden (Millisekunden, nicht Sekunden). Wenn ein Test langsam ist:<\/p>\n<ul>\n<li>Es wird nicht h\u00e4ufig genug ausgef\u00fchrt<\/li>\n<li>Entwickler \u00fcberspringen das Ausf\u00fchren der vollst\u00e4ndigen Testsuite<\/li>\n<li>CI wird teuer und langsam<\/li>\n<\/ul>\n<p><strong>L\u00f6sungen:<\/strong><\/p>\n<ul>\n<li>Verwenden Sie kleine synthetische Datens\u00e4tze anstelle gro\u00dfer realer Datens\u00e4tze<\/li>\n<li>Mock teure externe Berechnungen<\/li>\n<li>Separate langsame Integrationstests von schnellen Unit-Tests<\/li>\n<li>Verwenden Sie Parametrierung mit Bedacht: F\u00fchren Sie nicht in jedem Testpass Tausende von Variationen aus<\/li>\n<\/ul>\n<h3>4. Tests nicht isolieren<\/h3>\n<p>Tests sollten nicht voneinander oder vom globalen Zustand abh\u00e4ngen. Jeder Test sollte:<\/p>\n<ul>\n<li>eigene Testdaten erstellen (Fixtures verwenden)<\/li>\n<li>Aufr\u00e4umen nach sich<\/li>\n<li>Verlassen Sie sich nicht auf die Ausf\u00fchrungsreihenfolge<\/li>\n<\/ul>\n<pre><code class=\"language-python\"># \u274c Bad: shared mutable state\nresults = []\n\ndef test_first():\n    results.append(1)\n\ndef test_second():\n    assert results == [1]  # fails if tests run in wrong order\n\n# \u2705 Better: independent tests\ndef test_first():\n    result = compute_something()\n    assert result == 1\n\ndef test_second():\n    result = compute_something_else()\n    assert result == 2\n<\/code><\/pre>\n<h3>5. \u00dcberspringen von Tests ohne guten Grund<\/h3>\n<p><code>@pytest.mark.skip<\/code> sollte sparsam verwendet werden. Wenn ein Test \u00fcbersprungen wird, weil die Umgebung etwas fehlt, verwenden Sie <code>pytest.importorskip()<\/code> auf Modulebene oder machen Sie die Abh\u00e4ngigkeit in der CI-Konfiguration optional.<\/p>\n<h3>6. Vage Behauptungen schreiben<\/h3>\n<p>Tests sollten klar ausdr\u00fccken, was \u00fcberpr\u00fcft wird und warum.<\/p>\n<pre><code class=\"language-python\"># \u274c Unclear: what's being tested?\nassert result != None\n\n# \u2705 Clear: specific expectation with context\nassert result.converged is True, \"Solver should converge for well-posed problem\"\n<\/code><\/pre>\n<h3>7. Hardcoding-Pfade und Umgebungsannahmen<\/h3>\n<p>Verwenden Sie tempor\u00e4re Verzeichnisse und Fixtures anstelle fester Pfade. Das Fixture <code>tmp_path<\/code> von Pytest stellt f\u00fcr jeden Test ein neues tempor\u00e4res Verzeichnis bereit.<\/p>\n<h2>Praktisches Beispiel: Testen eines FIPY-Diffusionsl\u00f6sers<\/h2>\n<p>Lassen Sie uns diese Strategien mit einem konkreten Beispiel zusammenstellen, das f\u00fcr das Publikum von Matforge relevant ist.<\/p>\n<pre><code class=\"language-python\"># tests\/test_diffusion.py\nimport pytest\nimport numpy as np\nfrom fipy import Grid1D, CellVariable, DiffusionTerm\n\n@pytest.fixture\ndef simple_1d_grid():\n    \"\"\"Create a uniform 1D grid for diffusion testing.\"\"\"\n    return Grid1D(dx=0.1, nx=50)\n\n@pytest.fixture\ndef steady_state_diffusion(simple_1d_grid):\n    \"\"\"Set up diffusion with Dirichlet boundaries at both ends.\"\"\"\n    mesh = simple_1d_grid\n    var = CellVariable(name=\"concentration\", mesh=mesh, value=0.0)\n    var.constrain(1.0, mesh.facesLeft)\n    var.constrain(0.0, mesh.facesRight)\n    eq = DiffusionTerm(coeff=1.0) == 0\n    return var, eq\n\ndef test_mesh_volume(simple_1d_grid):\n    \"\"\"Total domain length should equal nx * dx.\"\"\"\n    expected_length = 50 * 0.1\n    assert simple_1d_grid.cellVolumes.sum() == pytest.approx(expected_length)\n\ndef test_diffusion_conservation(steady_state_diffusion):\n    \"\"\"For steady diffusion with no sources, total mass should be conserved.\"\"\"\n    var, eq = steady_state_diffusion\n    eq.solve(var, dt=1.0)\n    # With fixed values at boundaries, mass enters from left and exits right\n    # In steady state, flux in should equal flux out\n    left_flux = var.faceValue[simple_1d_grid.facesLeft.value]\n    right_flux = var.faceValue[simple_1d_grid.facesRight.value]\n    # Flux direction: positive means flow to the right\n    assert left_flux &gt; 0  # mass enters from left\n    assert right_flux &lt; 0  # mass exits from right (negative direction)\n    assert abs(left_flux + right_flux) == pytest.approx(0, abs=1e-10)\n\ndef test_diffusion_solution_shape(steady_state_diffusion):\n    \"\"\"Concentration should decrease monotonically from left to right.\"\"\"\n    var, eq = steady_state_diffusion\n    eq.solve(var, dt=1.0)\n    # Steady state should be linear for constant diffusivity\n    x = simple_1d_grid.cellCenters[0]\n    expected = 1.0 - x \/ (50 * 0.1)  # linear from 1 to 0\n    assert var.value == pytest.approx(expected, rel=1e-5)\n\n@pytest.mark.parametrize(\"dx,nx\", [(0.1, 50), (0.05, 100), (0.02, 250)])\ndef test_mesh_independence(dx, nx):\n    \"\"\"Solution should converge as mesh refines.\"\"\"\n    mesh = Grid1D(dx=dx, nx=nx)\n    var = CellVariable(name=\"c\", mesh=mesh, value=0.0)\n    var.constrain(1.0, mesh.facesLeft)\n    var.constrain(0.0, mesh.facesRight)\n    eq = DiffusionTerm(coeff=1.0) == 0\n    eq.solve(var, dt=1.0)\n    # Check at midpoint\n    mid_idx = nx \/\/ 2\n    assert var.value[mid_idx] == pytest.approx(0.5, rel=0.1)\n<\/code><\/pre>\n<p>Dieses Beispiel zeigt:<\/p>\n<ul>\n<li>Fixtures f\u00fcr wiederverwendbare Test-Setup<\/li>\n<li>Parametrisierung zum Testen mehrerer Aufl\u00f6sungen<\/li>\n<li>Toleranzbasierte numerische Behauptungen<\/li>\n<li>Testen physikalischer Prinzipien (Konservierung, Linearit\u00e4t)<\/li>\n<li>Klare, beschreibende Testnamen und Behauptungen<\/li>\n<\/ul>\n<h2>Wenn Unit-Tests nicht ausreichen<\/h2>\n<p>Unit-Tests validieren einzelne Komponenten, aber auch Forschungssoftware ben\u00f6tigt:<\/p>\n<ul>\n<li><strong>Integrationstests<\/strong>: Stellen Sie sicher, dass mehrere Module korrekt zusammenarbeiten<\/li>\n<li><strong>Systemtests<\/strong>: F\u00fchren Sie vollst\u00e4ndige Simulationen durch und vergleichen Sie sie mit bekannten Ausgaben<\/li>\n<li><strong>Leistungstests<\/strong>: Stellen Sie sicher, dass Algorithmen die Erwartungen an die Komplexit\u00e4t erf\u00fcllen<\/li>\n<li><strong>Visualisierungspr\u00fcfungen<\/strong>: Spot offensichtliche Rendering-Fehler (automatisierter Bildvergleich wo m\u00f6glich)<\/li>\n<\/ul>\n<p>Eine komplette Teststrategie f\u00fcr Forschungsprojekte umfasst mehrere Testebenen, wobei Einheitstests die Grundlage bilden.<\/p>\n<h2>Was wir empfehlen: Eine pragmatische Teststrategie f\u00fcr Forschungsprojekte<\/h2>\n<p>Basierend auf den Erkenntnissen aus Best Practices f\u00fcr wissenschaftliche Software finden Sie hier unseren empfohlenen Ansatz:<\/p>\n<h3>Beginnen Sie mit grundlegenden Unit-Tests<\/h3>\n<p>Beginnen Sie mit dem Schreiben von Tests f\u00fcr:<\/p>\n<ul>\n<li>mathematische Kernfunktionen (Sonderfunktionen, Koordinatentransformationen)<\/li>\n<li>Dienstprogramme zur Generierung und Manipulation von Netzen<\/li>\n<li>Implementierungen der Randbedingung<\/li>\n<li>Dateneingabe-\/Ausgaberoutinen (Validierung, Formatierung)<\/li>\n<\/ul>\n<p>Diese Komponenten sind stabil, haben klare erwartete Verhaltensweisen und werden in vielen Simulationen wiederverwendet.<\/p>\n<h3>\u00dcbernehmen Sie pytest.approx als Standard<\/h3>\n<p>Verwenden Sie niemals <code>==<\/code> f\u00fcr Gleitkommaergebnisse. Verwenden Sie immer <code>pytest.approx()<\/code> mit entsprechenden Toleranzen. Machen Sie dies zu einer Teamkonvention.<\/p>\n<h3>Verwenden Sie Fixtures ausgiebig<\/h3>\n<p>Fixtures reduzieren die Duplizierung und machen Tests haltbarer. Erstellen Sie Fixtures f\u00fcr:<\/p>\n<ul>\n<li>Gemeinsame Netze (1D, 2D, 3D-Testgitter)<\/li>\n<li>Standard-Grenzbedingungen-Setups<\/li>\n<li>bekannte analytische L\u00f6sungen<\/li>\n<li>Tempor\u00e4re Datei- \/ Verzeichnisverwaltung<\/li>\n<\/ul>\n<h3>Integrieren Sie CI fr\u00fchzeitig<\/h3>\n<p>Richten Sie GitHub-Aktionen (oder \u00e4hnliches) ein, bevor das Projekt gro\u00df wird. Testen Sie automatisch Tests auf:<\/p>\n<ul>\n<li>Jeder Sto\u00df<\/li>\n<li>jede Pull-Anfrage<\/li>\n<li>Geplante n\u00e4chtliche Builds (um Umweltdrift zu fangen)<\/li>\n<\/ul>\n<h3>Code-Abdeckung messen und verfolgen<\/h3>\n<p>Verwenden Sie <code>pytest-cov<\/code>, um zu messen, welche Teile Ihres Codes durch Tests ausge\u00fcbt werden. Zielen Sie auf mindestens 80% der Kernmodule, aber nicht \u00fcber 100% besessen &#8211; das Ziel ist Vertrauen, keine perfekte Punktzahl.<\/p>\n<h3>Schreiben Sie Tests, wenn Sie Fehler beheben<\/h3>\n<p>Wenn ein Fehler gemeldet wird, schreiben Sie einen Test, der ihn reproduziert, bevor Sie es beheben. Dies garantiert, dass der Fehler sp\u00e4ter nicht mehr angezeigt wird.<\/p>\n<h3>Testen Sie schnell<\/h3>\n<p>Wenn ein Test l\u00e4nger als ein paar Sekunden dauert, beachten Sie:<\/p>\n<ul>\n<li>Verwenden kleinerer Testprobleme<\/li>\n<li>Verspotten teure Operationen<\/li>\n<li>Verschieben Sie es in eine weniger h\u00e4ufig ausgef\u00fchrte Integrationstest-Suite<\/li>\n<\/ul>\n<h2>Verwandte Anleitungen<\/h2>\n<ul>\n<li><a href=\"\/tracking-long-term-technical-debt-in-research-software\/\">Nachverfolgung langfristiger technischer Schulden in der Forschungssoftware<\/a> \u2013 Verwalten von Testschulden, wenn sich Projekte weiterentwickeln<\/li>\n<li><a href=\"\/managing-research-software-through-tickets\/\">Verwalten von Forschungssoftware \u00fcber Tickets<\/a> \u2013 Mithilfe von Issue-Tracking zur Koordinierung der Testbem\u00fchungen<\/li>\n<li><a href=\"\/reproducibility-and-its-role-in-debugging\/\">Reproduzierbarkeit und ihre Rolle beim Debuggen<\/a> \u2013 Wie Tests systematisches Debuggen erm\u00f6glichen<\/li>\n<li><a href=\"\/how-to-write-a-clear-and-useful-bug-report\/\">Wie schreibe ich einen klaren und n\u00fctzlichen Fehlerbericht<\/a> \u2013 Bereitstellung der Informationen, die zum Erstellen von Regressionstests erforderlich sind<\/li>\n<li><a href=\"\/feature-requests-vs-bug-reports-knowing-the-difference\/\">Funktionsanfragen vs. Fehlerberichte: Wissen \u00fcber den Unterschied<\/a> \u2013 Klassifizierung von Problemen, die die Testentwicklung vorantreiben<\/li>\n<\/ul>\n<h2>Schlussfolgerung<\/h2>\n<p>Unit Testing verwandelt Forschungssoftware aus fragilen Skripten in zuverl\u00e4ssige, reproduzierbare Instrumente. W\u00e4hrend das Einrichten umfassender Tests Vorabinvestitionen erfordert, kommt es zu einer k\u00fcrzeren Debugging-Zeit, einem h\u00f6heren Vertrauen in die Ergebnisse und einer reibungslosen Zusammenarbeit.<\/p>\n<p>Das PyTest-Framework bietet leistungsstarke Tools &#8211; Fixtures, Parametrisierung, Verspotten und <code>approx()<\/code>  -, die sich direkt mit den Herausforderungen des wissenschaftlichen Codes befassen: numerische Pr\u00e4zision, externe Abh\u00e4ngigkeiten und unbekannte richtige Antworten. In Kombination mit der kontinuierlichen Integration stellen diese Praktiken sicher, dass Tests konsistent \u00fcber Umgebungen hinweg ausgef\u00fchrt werden.<\/p>\n<p>Denken Sie daran: Beim Testen geht es nicht darum, Perfektion zu erreichen. Es geht darum, gen\u00fcgend Vertrauen in Ihren Code aufzubauen, dass Sie seinen Ausgaben vertrauen k\u00f6nnen, wenn es darauf ankommt. Beginnen Sie mit den Kernkomponenten, schreiben Sie Tests, die klare Erwartungen zum Ausdruck bringen, und erweitern Sie die Abdeckung schrittweise, wenn das Projekt w\u00e4chst.<\/p>\n<p>Ihr zuk\u00fcnftiges Selbst &#8211; und jeder, der Ihren Code erbt &#8211; wird es Ihnen danken.<\/p>\n<h2>N\u00e4chste Schritte<\/h2>\n<p>Sind Sie bereit, Ihrem Forschungsprojekt Tests hinzuzuf\u00fcgen?<\/p>\n<ol>\n<li>Installieren Sie Pytest: <code>pip install pytest<\/code><\/li>\n<li>Erstellen Sie ein <code>tests\/<\/code>-Verzeichnis mit einer einfachen Testdatei<\/li>\n<li>Schreiben Sie einen Test f\u00fcr eine Kernfunktion mit <code>pytest.approx<\/code><\/li>\n<li>Einrichten eines GitHub-Aktionen-Workflows, um Tests automatisch auszuf\u00fchren<\/li>\n<li>Erweitern Sie die Abdeckung schrittweise, w\u00e4hrend Sie den Code \u00e4ndern<\/li>\n<\/ol>\n<p>F\u00fcr die personalisierte Hilfe bei der Implementierung von Teststrategien in Ihrer spezifischen Forschungssoftware wenden Sie sich bitte an <a href=\"\/\"> f\u00fcr eine Beratung <\/a> (siehe unsere Homepage f\u00fcr weitere Informationen).<\/p>\n","protected":false,"raw":"<p>Unit-Tests sind f\u00fcr vertrauensw\u00fcrdige wissenschaftliche Software nicht verhandelbar. Im Gegensatz zu kommerziellen Anwendungen fehlen dem Forschungscode h\u00e4ufig formelle Tests, was zu nicht reproduzierbaren Ergebnissen und vergeudeten Anstrengungen f\u00fchrt. Dieser Leitfaden behandelt Pytest-Strategien speziell f\u00fcr wissenschaftliche Python-Projekte: Umgang mit numerischer Pr\u00e4zision mit <code>pytest.approx<\/code>, Isolierung externer Abh\u00e4ngigkeiten mit Spott, Verwendung von Fixtures und Parametrisierung effizient und Integration von Tests in kontinuierliche Integrations-Pipelines. Sie erfahren, wann die Black-Box-Validierung gegen ver\u00f6ffentlichte Ergebnisse verwendet werden muss und wie Sie Tests entwerfen, die die Code-Entwicklung \u00fcberleben, ohne br\u00fcchig zu werden.<\/p>\n<h2>Warum Unit-Testing in der Forschungssoftware wichtig ist<\/h2>\n<p>Wissenschaftliche Software existiert in einem anspruchsvollen Raum. Es muss flexibel genug sein, um neue Hypothesen zu untersuchen, aber zuverl\u00e4ssig genug, dass ver\u00f6ffentlichte Ergebnisse Monate oder Jahre sp\u00e4ter reproduziert werden k\u00f6nnen. Im Gegensatz zu kommerzieller Software mit klaren Spezifikationen entwickelt sich h\u00e4ufig neben Experimenten auch der Forschungscode, wobei sich die Anforderungen \u00e4ndern, wenn sich neue Entdeckungen ergeben.<\/p>\n<p>Die Folgen unzureichender Untersuchungen in der Forschung sind schwerwiegend:<\/p>\n<ul>\n<li><strong>Fehlproduzierbare Ergebnisse<\/strong>: Verschiedene Forscher erhalten unterschiedliche Ausgaben aus demselben Code<\/li>\n<li><strong>Silent Bugs<\/strong>: numerische Fehler, die individuell zu signifikanten Ungenauigkeiten erscheinen<\/li>\n<li><strong>Wissensverlust<\/strong>: Wenn urspr\u00fcngliche Entwickler gehen, dienen Tests als ausf\u00fchrbare Dokumentation<\/li>\n<li><strong>Vergeudete Anstrengung<\/strong>: Debuggen wird zu einer Detektivjagd anstelle eines systematischen Prozesses<\/li>\n<\/ul>\n<p>Unit-Tests l\u00f6sen diese Probleme durch die Validierung kleiner, isolierter Komponenten Ihres Codes. Jeder Test \u00fcberpr\u00fcft, ob sich eine bestimmte Funktion oder Klasse wie erwartet bei definierten Eingaben verh\u00e4lt. Wenn Tests konsistent \u00fcber Umgebungen hinweg bestehen, haben Sie den Nachweis, dass Ihr Code zuverl\u00e4ssige Ergebnisse liefert.<\/p>\n<p>Das Testen des wissenschaftlichen Codes stellt jedoch einzigartige Herausforderungen dar, die Standard-Software-Testans\u00e4tze nicht vollst\u00e4ndig angehen.<\/p>\n<h2>Einzigartige Herausforderungen beim Testen von wissenschaftlichem Code<\/h2>\n<p>Der wissenschaftliche und numerische Code unterscheidet sich von typischen Gesch\u00e4ftsanwendungen in verschiedenen Bereichen, die sich auf die Teststrategie auswirken.<\/p>\n<h3>Numerische Pr\u00e4zision und Gleitkommafehler<\/h3>\n<p>Flie\u00dfkommaarithmetik ist von Natur aus ungenau. Da Computer Dezimalzahlen darstellen, ist <code>0.1 + 0.2<\/code> nicht genau <code>0.3<\/code> in der bin\u00e4ren Gleitkommadarstellung. In wissenschaftlichen Simulationen, die Tausende oder Millionen von Operationen beinhalten, sammeln sich diese winzigen Fehler an.<\/p>\n<p>Ein naiver Test, der exakte Gleichheit verwendet (<code>==<\/code>) wird zeitweise oder auf unterschiedlicher Hardware fehlschlagen:<\/p>\n<pre><code class=\"language-python\">def test_numerical_computation():\n    result = complex_simulation()  # returns 1.0000000000000002\n    assert result == 1.0  # FAILS! Even though the difference is negligible\n<\/code><\/pre>\n<p>Die L\u00f6sung besteht darin, toleranzbasierte Vergleiche zu verwenden. PYTEST stellt hierf\u00fcr <code>pytest.approx()<\/code> zur Verf\u00fcgung:<\/p>\n<pre><code class=\"language-python\">def test_numerical_computation():\n    result = complex_simulation()\n    assert result == pytest.approx(1.0, rel=1e-9, abs=1e-12)\n<\/code><\/pre>\n<p><code>rel<\/code> (relative Toleranz) ist n\u00fctzlich f\u00fcr Werte jeglicher Gr\u00f6\u00dfe; <code>abs<\/code> (Absolute Toleranz) Behandelt F\u00e4lle, in denen der erwartete Wert nahe Null liegt. Die Standardeinstellungen sind <code>rel=1e-6<\/code> und <code>abs=1e-12<\/code>, aber der wissenschaftliche Code erfordert h\u00e4ufig engere Toleranzen.<\/p>\n<h3>Unbekannte \"richtige\" Antworten<\/h3>\n<p>In vielen Forschungsszenarien haben Sie keine bekannte korrekte Ausgabe. Die Simulation k\u00f6nnte Neuland erforschen. Wie testen Sie Code, wenn Sie nicht wissen, wie die Antwort lauten soll?<\/p>\n<p>Mehrere Strategien funktionieren:<\/p>\n<ol>\n<li><strong>Inverse Operations<\/strong>: Wenn Ihr Code <code>B = f(A)<\/code> berechnet, testen Sie auch <code>A \u2248 f\u207b\u00b9(B)<\/code>.<\/li>\n<li><strong>Erhaltungsgesetze<\/strong>: Vergewissern Sie sich, dass Masse, Energie oder Impuls innerhalb der Toleranz erhalten bleiben.<\/li>\n<li><strong>Begrenzende F\u00e4lle<\/strong>: Testverhalten in vereinfachten Grenzen, wenn analytische L\u00f6sungen existieren.<\/li>\n<li><strong>Regressionstests<\/strong>: Speichern Sie die Ausgaben eines vertrauensw\u00fcrdigen Laufs und erkennen Sie unerwartete \u00c4nderungen.<\/li>\n<\/ol>\n<h3>Externe Abh\u00e4ngigkeiten und starke Berechnung<\/h3>\n<p>Wissenschaftlicher Code h\u00e4ngt oft von:<\/p>\n<ul>\n<li>Gro\u00dfe Datens\u00e4tze (Terabyte der Simulationseingabe)<\/li>\n<li>Externe Solver oder Bibliotheken (HPC-Pakete, Fortran-Bibliotheken)<\/li>\n<li>Datei-E \/ A mit komplexen Formaten<\/li>\n<li>Datenbankverbindungen oder APIs<\/li>\n<\/ul>\n<p>Das Ausf\u00fchren des vollen Systems in jedem Unit-Test ist unpraktisch. Sie brauchen Isolation.<\/p>\n<h2>PYTest-Funktionen, die Probleme beim Testen von Forschungstests l\u00f6sen<\/h2>\n<p>Pytest bietet mehrere Funktionen, die f\u00fcr den wissenschaftlichen Code besonders wertvoll sind.<\/p>\n<h3>Fixtures f\u00fcr Setup und Teardown<\/h3>\n<p>Fixtures kapseln den Setup-Code, der vor Tests ausgef\u00fchrt wird. F\u00fcr wissenschaftliche Tests k\u00f6nnen Fixtures:<\/p>\n<ul>\n<li>Erstellen Sie tempor\u00e4re Testdaten oder Netze<\/li>\n<li>Initialisieren Sie Simulationsobjekte mit bekannten Parametern<\/li>\n<li>Beseitigen Sie tempor\u00e4re Dateien nach den Tests<\/li>\n<li>Stellen Sie wiederverwendbare Testkonfigurationen bereit<\/li>\n<\/ul>\n<pre><code class=\"language-python\">import pytest\nimport tempfile\nimport numpy as np\n\n@pytest.fixture\ndef simple_mesh():\n    \"\"\"Create a small 1D mesh for testing.\"\"\"\n    from fipy import Grid1D\n    return Grid1D(dx=0.1, nx=10)\n\n@pytest.fixture\ndef diffusion_solver(simple_mesh):\n    \"\"\"Set up a diffusion solver on the test mesh.\"\"\"\n    from fipy import CellVariable, DiffusionTerm\n    var = CellVariable(name=\"concentration\", mesh=simple_mesh, value=1.0)\n    eq = DiffusionTerm(coeff=1.0) == 0\n    return var, eq\n<\/code><\/pre>\n<h3>Parametrierung f\u00fcr mehrere Szenarien<\/h3>\n<p>Anstatt f\u00fcr \u00e4hnliche F\u00e4lle separate Testfunktionen zu schreiben, verwenden Sie <code>@pytest.mark.parametrize<\/code>, um dieselbe Testlogik mit unterschiedlichen Eing\u00e4ngen auszuf\u00fchren.<\/p>\n<pre><code class=\"language-python\">@pytest.mark.parametrize(\"dx,nx,expected_volume\", [\n    (0.1, 10, 1.0),\n    (0.01, 100, 1.0),\n    (0.001, 1000, 1.0),\n])\ndef test_mesh_volume(dx, nx, expected_volume):\n    \"\"\"Test that mesh volume matches domain size.\"\"\"\n    mesh = Grid1D(dx=dx, nx=nx)\n    assert mesh.cellVolumes.sum() == pytest.approx(expected_volume)\n<\/code><\/pre>\n<p>Parametrierung ist besonders n\u00fctzlich f\u00fcr:<\/p>\n<ul>\n<li>Testen von Kanten (Nullwerte, sehr kleine\/gro\u00dfe Zahlen)<\/li>\n<li>\u00dcberpr\u00fcfen des Verhaltens \u00fcber verschiedene Netzaufl\u00f6sungen<\/li>\n<li>Validierung mehrerer Randbedingungstypen<\/li>\n<li>\u00dcberpr\u00fcfen verschiedener Werte f\u00fcr Materialeigenschaften<\/li>\n<\/ul>\n<p>F\u00fcr Research Code k\u00f6nnen Sie gegen bekannte Benchmark-Ergebnisse aus ver\u00f6ffentlichten Artikeln parametrisieren.<\/p>\n<h3>Verspotten von externen Abh\u00e4ngigkeiten<\/h3>\n<p>Das Verspotten ersetzt echte Abh\u00e4ngigkeiten durch kontrollierte F\u00e4lschungen. Dies isoliert das zu testende Ger\u00e4t und macht Tests schneller und zuverl\u00e4ssiger.<\/p>\n<p><strong>Wann im wissenschaftlichen Code verspotten:<\/strong><\/p>\n<ul>\n<li>Externe Datendateien: Ersetzen Sie gro\u00dfe Datens\u00e4tze durch minimale synthetische Daten, die dieselben Codepfade aus\u00fcben<\/li>\n<li>HPC-Solver: Mock teure FORTRAN-Bibliotheken mit reinen Python-Implementierungen, die bekannte Ergebnisse zur\u00fcckgeben<\/li>\n<li>Netzwerk-APIs: Stub-Remote-Dienste, die Parameter oder Konfiguration bereitstellen<\/li>\n<li>Zufallszahlengeneratoren: Samen Sie sie, um deterministische Sequenzen zu erzeugen<\/li>\n<\/ul>\n<pre><code class=\"language-python\">from unittest.mock import patch, MagicMock\n\ndef test_simulation_with_external_data():\n    # Mock the data loading function to return small, known data\n    with patch('mycode.load_large_dataset') as mock_load:\n        mock_load.return_value = np.array([1.0, 2.0, 3.0])\n        result = run_simulation()\n        assert result.converged\n<\/code><\/pre>\n<p><strong>Wichtige Richtlinie<\/strong>: Verspotten Sie die Abh\u00e4ngigkeiten Ihres eigenen Codes, nicht die Bibliotheken von Drittanbietern, die Sie nicht kontrollieren. Folgen Sie dem Prinzip \"Verspotten Sie nicht, was Sie nicht besitzen.\"<\/p>\n<h3>Verwenden von <code>pytest.approx<\/code> f\u00fcr numerische Vergleiche<\/h3>\n<p>Gleitkommavergleiche m\u00fcssen Rundungsfehler ber\u00fccksichtigen. Das Objekt von Pytest <code>approx<\/code> behandelt dies elegant:<\/p>\n<pre><code class=\"language-python\">def test_diffusion_solution():\n    \"\"\"Test that diffusion reaches expected steady state.\"\"\"\n    concentration = solve_diffusion(time=100.0)\n    expected = 0.5  # analytical steady state for this boundary condition\n    assert concentration.mean() == pytest.approx(expected, rel=1e-6)\n<\/code><\/pre>\n<p>Sie k\u00f6nnen auch <code>approx<\/code> mit Arrays verwenden:<\/p>\n<pre><code class=\"language-python\">def test_array_computation():\n    result = compute_field()\n    expected = np.array([1.0, 2.0, 3.0])\n    assert result == pytest.approx(expected)\n<\/code><\/pre>\n<p>W\u00e4hlen Sie f\u00fcr den wissenschaftlichen Code Toleranzen basierend auf:<\/p>\n<ul>\n<li>Die numerische Genauigkeit Ihrer Methoden (z.<\/li>\n<li>Die von Ihrer Anwendung erforderliche Pr\u00e4zision (Engineering Toleranz vs. Explorationsforschung)<\/li>\n<\/ul>\n<h2>Testgetriebene Entwicklung f\u00fcr Forschungsprojekte<\/h2>\n<p>Test-Driven Development (TDD) folgt einem einfachen Zyklus: Schreiben Sie einen fehlgeschlagenen Test, schreiben Sie dann minimalen Code, um ihn zu bestehen, und dann \u00fcberarbeiten Sie ihn. W\u00e4hrend TDD in kommerzieller Software gut etabliert ist, widersetzen sich Forschungsprojekte h\u00e4ufig aufgrund wahrgenommener Zeitbeschr\u00e4nkungen.<\/p>\n<p>Die Realit\u00e4t: TDD spart Zeit in der Forschung, indem sie Fehler abfangen, bevor sie sich durch Experimente ausbreiten. Das Schreiben von Tests zwingt Sie dazu, die Schnittstelle und das erwartete Verhalten jeder Funktion vor der Implementierung zu kl\u00e4ren.<\/p>\n<p><strong>TDD f\u00fcr die wissenschaftliche Erforschung angepasst:<\/strong><\/p>\n<ol>\n<li>Beginnen Sie mit einem einfachen Modell oder Algorithmus, das Sie analytisch verstehen<\/li>\n<li>Schreiben Sie Tests, die gegen bekannte Ergebnisse validieren (Analytische L\u00f6sungen, Begrenzungsf\u00e4lle)<\/li>\n<li>Implementieren Sie den Code, um diese Tests zu bestehen<\/li>\n<li>Erweitern Sie das Modell inkrementell und f\u00fcgen Sie Tests f\u00fcr jede neue Funktion hinzu<\/li>\n<li>Wenn Sie einen Fehler entdecken, schreiben Sie einen Test, der ihn zuerst reproduziert, und beheben Sie ihn dann<\/li>\n<\/ol>\n<p>TDD funktioniert gut f\u00fcr:<\/p>\n<ul>\n<li>Utility-Funktionen (Mesh-Generierung, Koordinatentransformationen)<\/li>\n<li>Mathematische Operationen (Matrix-Manipulationen, Sonderfunktionen)<\/li>\n<li>Datenverarbeitungs-Pipelines (Parsing, Filtern, Normalisierung)<\/li>\n<li>Konfigurationsvalidierung<\/li>\n<\/ul>\n<p>TDD ist weniger geeignet f\u00fcr:<\/p>\n<ul>\n<li>Sehr exploratorischer Code, bei dem die Schnittstelle selbst unsicher ist<\/li>\n<li>Einmalige Skripte, die nicht wiederverwendet werden<\/li>\n<li>Code, der von noch nicht verf\u00fcgbaren externen Ressourcen abh\u00e4ngt<\/li>\n<\/ul>\n<p>In der Praxis funktioniert ein hybrider Ansatz am besten: Schreibtests f\u00fcr stabile, grundlegende Komponenten; Verwenden Sie leichtere Integrationstests f\u00fcr experimentelle Abschnitte.<\/p>\n<h2>Organisation von Tests f\u00fcr wissenschaftliche Projekte<\/h2>\n<p>Wo sollen Testdateien leben? Pytest bietet Flexibilit\u00e4t:<\/p>\n<pre><code>my_research_project\/\n\u251c\u2500\u2500 src\/\n\u2502   \u2514\u2500\u2500 mypackage\/\n\u2502       \u251c\u2500\u2500 __init__.py\n\u2502       \u251c\u2500\u2500 solver.py\n\u2502       \u2514\u2500\u2500 mesh.py\n\u251c\u2500\u2500 tests\/\n\u2502   \u251c\u2500\u2500 __init__.py\n\u2502   \u251c\u2500\u2500 test_solver.py\n\u2502   \u251c\u2500\u2500 test_mesh.py\n\u2502   \u2514\u2500\u2500 conftest.py  # shared fixtures\n\u251c\u2500\u2500 data\/\n\u2502   \u2514\u2500\u2500 reference_results\/  # stored outputs for regression tests\n\u251c\u2500\u2500 .github\/\n\u2502   \u2514\u2500\u2500 workflows\/\n\u2502       \u2514\u2500\u2500 ci.yml  # GitHub Actions CI configuration\n\u251c\u2500\u2500 pyproject.toml\n\u2514\u2500\u2500 README.md\n<\/code><\/pre>\n<p><strong>Schl\u00fcsselkonventionen:<\/strong><\/p>\n<ul>\n<li>Testen Sie die Tests in einem separaten Verzeichnis <code>tests\/<\/code> parallel zu <code>src\/<\/code> (oder <code>lib\/<\/code>)<\/li>\n<li>Name Testdateien <code>test_*.py<\/code> oder <code>*_test.py<\/code><\/li>\n<li>Name Testfunktionen <code>test_*()<\/code>, um PyTest automatisch zu erkennen<\/li>\n<li>Verwenden Sie <code>conftest.py<\/code> f\u00fcr Ger\u00e4te, die \u00fcber mehrere Testdateien geteilt werden<\/li>\n<\/ul>\n<p>Bei FIPY-basierten Projekten stimmen Strukturtests der Modulhierarchie \u00fcberein:<\/p>\n<pre><code>fipy_project\/\n\u251c\u2500\u2500 fipy\/\n\u2502   \u251c\u2500\u2500 meshes\/\n\u2502   \u2502   \u2514\u2500\u2500 grid1d.py\n\u2502   \u2514\u2500\u2500 terms\/\n\u2502       \u2514\u2500\u2500 diffusion.py\n\u251c\u2500\u2500 tests\/\n\u2502   \u251c\u2500\u2500 meshes\/\n\u2502   \u2502   \u2514\u2500\u2500 test_grid1d.py\n\u2502   \u2514\u2500\u2500 terms\/\n\u2502       \u2514\u2500\u2500 test_diffusion.py\n<\/code><\/pre>\n<h2>Integration mit kontinuierlicher Integration<\/h2>\n<p>Unit-Tests liefern nur einen Wert, wenn sie konsistent ausgef\u00fchrt werden. Continuous Integration (CI) automatisiert die Testausf\u00fchrung, wenn sich der Code \u00e4ndert.<\/p>\n<p>GitHub Actions bietet ein einfaches CI-Setup f\u00fcr Python-Projekte:<\/p>\n<pre><code class=\"language-yaml\"># .github\/workflows\/ci.yml\nname: CI\n\non: [push, pull_request]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [\"3.9\", \"3.10\", \"3.11\"]\n\n    steps:\n    - uses: actions\/checkout@v3\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions\/setup-python@v4\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install -e .[test]\n    - name: Run tests with pytest\n      run: |\n        pytest --cov=src --cov-report=xml --cov-report=html\n    - name: Upload coverage\n      uses: codecov\/codecov-action@v3\n<\/code><\/pre>\n<p>CI stellt sicher:<\/p>\n<ul>\n<li>Tests bestehen auf mehreren Plattformen (Linux, macOS, Windows)<\/li>\n<li>Tests \u00fcbergeben mehrere Python-Versionen<\/li>\n<li>Die Codeabdeckung wird im Laufe der Zeit verfolgt<\/li>\n<li>Pull-Anforderungen werden vor dem Zusammenf\u00fchren validiert<\/li>\n<\/ul>\n<p>F\u00fcr Forschungssoftware sollten Sie Folgendes hinzuf\u00fcgen:<\/p>\n<ul>\n<li>Tests, die mit verschiedenen Versionen von Schl\u00fcsselabh\u00e4ngigkeiten ausgef\u00fchrt werden (numpy, scipy, fipy)<\/li>\n<li>Leistungsregressionspr\u00fcfungen (stellen Sie sicher, dass die Algorithmen nicht langsamer werden)<\/li>\n<li>Dokumentations-Builds zum \u00dcberpr\u00fcfen von Beispielen funktionieren immer noch<\/li>\n<\/ul>\n<h2>h\u00e4ufige Fehler und wie man sie vermeidet<\/h2>\n<p>Basierend auf den Best Practices der Forschung und der Branche finden Sie hier die h\u00e4ufigsten Fehler im wissenschaftlichen Code:<\/p>\n<h3>1. Testen von Implementierungsdetails anstelle von Verhalten<\/h3>\n<p>Das Testen der internen Implementierung macht Tests spr\u00f6de. Wenn Sie den Code \u00fcberarbeiten, sollten die Tests weiterhin bestehen, wenn das externe Verhalten korrekt ist.<\/p>\n<pre><code class=\"language-python\"># \u274c Bad: tests internal state\ndef test_algorithm_updates_counter():\n    obj = MyAlgorithm()\n    obj.step()\n    assert obj.counter == 1  # fragile if counter implementation changes\n\n# \u2705 Better: tests observable outcome\ndef test_algorithm_produces_correct_result():\n    obj = MyAlgorithm()\n    result = obj.run()\n    assert result == expected\n<\/code><\/pre>\n<h3>2. Numerische Toleranz ignorieren<\/h3>\n<p>Exakte Vergleiche bei Gleitkomma-Ergebnissen verursachen flockige Tests, die zuf\u00e4llig oder auf unterschiedlicher Hardware fehlschlagen. Verwenden Sie f\u00fcr numerische Ausgaben immer <code>pytest.approx()<\/code> oder \u00e4hnliche toleranzbasierte Zusicherungen.<\/p>\n<h3>3. Schreiben Sie langsame Tests<\/h3>\n<p>Unit-Tests sollten schnell ausgef\u00fchrt werden (Millisekunden, nicht Sekunden). Wenn ein Test langsam ist:<\/p>\n<ul>\n<li>Es wird nicht h\u00e4ufig genug ausgef\u00fchrt<\/li>\n<li>Entwickler \u00fcberspringen das Ausf\u00fchren der vollst\u00e4ndigen Testsuite<\/li>\n<li>CI wird teuer und langsam<\/li>\n<\/ul>\n<p><strong>L\u00f6sungen:<\/strong><\/p>\n<ul>\n<li>Verwenden Sie kleine synthetische Datens\u00e4tze anstelle gro\u00dfer realer Datens\u00e4tze<\/li>\n<li>Mock teure externe Berechnungen<\/li>\n<li>Separate langsame Integrationstests von schnellen Unit-Tests<\/li>\n<li>Verwenden Sie Parametrierung mit Bedacht: F\u00fchren Sie nicht in jedem Testpass Tausende von Variationen aus<\/li>\n<\/ul>\n<h3>4. Tests nicht isolieren<\/h3>\n<p>Tests sollten nicht voneinander oder vom globalen Zustand abh\u00e4ngen. Jeder Test sollte:<\/p>\n<ul>\n<li>eigene Testdaten erstellen (Fixtures verwenden)<\/li>\n<li>Aufr\u00e4umen nach sich<\/li>\n<li>Verlassen Sie sich nicht auf die Ausf\u00fchrungsreihenfolge<\/li>\n<\/ul>\n<pre><code class=\"language-python\"># \u274c Bad: shared mutable state\nresults = []\n\ndef test_first():\n    results.append(1)\n\ndef test_second():\n    assert results == [1]  # fails if tests run in wrong order\n\n# \u2705 Better: independent tests\ndef test_first():\n    result = compute_something()\n    assert result == 1\n\ndef test_second():\n    result = compute_something_else()\n    assert result == 2\n<\/code><\/pre>\n<h3>5. \u00dcberspringen von Tests ohne guten Grund<\/h3>\n<p><code>@pytest.mark.skip<\/code> sollte sparsam verwendet werden. Wenn ein Test \u00fcbersprungen wird, weil die Umgebung etwas fehlt, verwenden Sie <code>pytest.importorskip()<\/code> auf Modulebene oder machen Sie die Abh\u00e4ngigkeit in der CI-Konfiguration optional.<\/p>\n<h3>6. Vage Behauptungen schreiben<\/h3>\n<p>Tests sollten klar ausdr\u00fccken, was \u00fcberpr\u00fcft wird und warum.<\/p>\n<pre><code class=\"language-python\"># \u274c Unclear: what's being tested?\nassert result != None\n\n# \u2705 Clear: specific expectation with context\nassert result.converged is True, \"Solver should converge for well-posed problem\"\n<\/code><\/pre>\n<h3>7. Hardcoding-Pfade und Umgebungsannahmen<\/h3>\n<p>Verwenden Sie tempor\u00e4re Verzeichnisse und Fixtures anstelle fester Pfade. Das Fixture <code>tmp_path<\/code> von Pytest stellt f\u00fcr jeden Test ein neues tempor\u00e4res Verzeichnis bereit.<\/p>\n<h2>Praktisches Beispiel: Testen eines FIPY-Diffusionsl\u00f6sers<\/h2>\n<p>Lassen Sie uns diese Strategien mit einem konkreten Beispiel zusammenstellen, das f\u00fcr das Publikum von Matforge relevant ist.<\/p>\n<pre><code class=\"language-python\"># tests\/test_diffusion.py\nimport pytest\nimport numpy as np\nfrom fipy import Grid1D, CellVariable, DiffusionTerm\n\n@pytest.fixture\ndef simple_1d_grid():\n    \"\"\"Create a uniform 1D grid for diffusion testing.\"\"\"\n    return Grid1D(dx=0.1, nx=50)\n\n@pytest.fixture\ndef steady_state_diffusion(simple_1d_grid):\n    \"\"\"Set up diffusion with Dirichlet boundaries at both ends.\"\"\"\n    mesh = simple_1d_grid\n    var = CellVariable(name=\"concentration\", mesh=mesh, value=0.0)\n    var.constrain(1.0, mesh.facesLeft)\n    var.constrain(0.0, mesh.facesRight)\n    eq = DiffusionTerm(coeff=1.0) == 0\n    return var, eq\n\ndef test_mesh_volume(simple_1d_grid):\n    \"\"\"Total domain length should equal nx * dx.\"\"\"\n    expected_length = 50 * 0.1\n    assert simple_1d_grid.cellVolumes.sum() == pytest.approx(expected_length)\n\ndef test_diffusion_conservation(steady_state_diffusion):\n    \"\"\"For steady diffusion with no sources, total mass should be conserved.\"\"\"\n    var, eq = steady_state_diffusion\n    eq.solve(var, dt=1.0)\n    # With fixed values at boundaries, mass enters from left and exits right\n    # In steady state, flux in should equal flux out\n    left_flux = var.faceValue[simple_1d_grid.facesLeft.value]\n    right_flux = var.faceValue[simple_1d_grid.facesRight.value]\n    # Flux direction: positive means flow to the right\n    assert left_flux &gt; 0  # mass enters from left\n    assert right_flux &lt; 0  # mass exits from right (negative direction)\n    assert abs(left_flux + right_flux) == pytest.approx(0, abs=1e-10)\n\ndef test_diffusion_solution_shape(steady_state_diffusion):\n    \"\"\"Concentration should decrease monotonically from left to right.\"\"\"\n    var, eq = steady_state_diffusion\n    eq.solve(var, dt=1.0)\n    # Steady state should be linear for constant diffusivity\n    x = simple_1d_grid.cellCenters[0]\n    expected = 1.0 - x \/ (50 * 0.1)  # linear from 1 to 0\n    assert var.value == pytest.approx(expected, rel=1e-5)\n\n@pytest.mark.parametrize(\"dx,nx\", [(0.1, 50), (0.05, 100), (0.02, 250)])\ndef test_mesh_independence(dx, nx):\n    \"\"\"Solution should converge as mesh refines.\"\"\"\n    mesh = Grid1D(dx=dx, nx=nx)\n    var = CellVariable(name=\"c\", mesh=mesh, value=0.0)\n    var.constrain(1.0, mesh.facesLeft)\n    var.constrain(0.0, mesh.facesRight)\n    eq = DiffusionTerm(coeff=1.0) == 0\n    eq.solve(var, dt=1.0)\n    # Check at midpoint\n    mid_idx = nx \/\/ 2\n    assert var.value[mid_idx] == pytest.approx(0.5, rel=0.1)\n<\/code><\/pre>\n<p>Dieses Beispiel zeigt:<\/p>\n<ul>\n<li>Fixtures f\u00fcr wiederverwendbare Test-Setup<\/li>\n<li>Parametrisierung zum Testen mehrerer Aufl\u00f6sungen<\/li>\n<li>Toleranzbasierte numerische Behauptungen<\/li>\n<li>Testen physikalischer Prinzipien (Konservierung, Linearit\u00e4t)<\/li>\n<li>Klare, beschreibende Testnamen und Behauptungen<\/li>\n<\/ul>\n<h2>Wenn Unit-Tests nicht ausreichen<\/h2>\n<p>Unit-Tests validieren einzelne Komponenten, aber auch Forschungssoftware ben\u00f6tigt:<\/p>\n<ul>\n<li><strong>Integrationstests<\/strong>: Stellen Sie sicher, dass mehrere Module korrekt zusammenarbeiten<\/li>\n<li><strong>Systemtests<\/strong>: F\u00fchren Sie vollst\u00e4ndige Simulationen durch und vergleichen Sie sie mit bekannten Ausgaben<\/li>\n<li><strong>Leistungstests<\/strong>: Stellen Sie sicher, dass Algorithmen die Erwartungen an die Komplexit\u00e4t erf\u00fcllen<\/li>\n<li><strong>Visualisierungspr\u00fcfungen<\/strong>: Spot offensichtliche Rendering-Fehler (automatisierter Bildvergleich wo m\u00f6glich)<\/li>\n<\/ul>\n<p>Eine komplette Teststrategie f\u00fcr Forschungsprojekte umfasst mehrere Testebenen, wobei Einheitstests die Grundlage bilden.<\/p>\n<h2>Was wir empfehlen: Eine pragmatische Teststrategie f\u00fcr Forschungsprojekte<\/h2>\n<p>Basierend auf den Erkenntnissen aus Best Practices f\u00fcr wissenschaftliche Software finden Sie hier unseren empfohlenen Ansatz:<\/p>\n<h3>Beginnen Sie mit grundlegenden Unit-Tests<\/h3>\n<p>Beginnen Sie mit dem Schreiben von Tests f\u00fcr:<\/p>\n<ul>\n<li>mathematische Kernfunktionen (Sonderfunktionen, Koordinatentransformationen)<\/li>\n<li>Dienstprogramme zur Generierung und Manipulation von Netzen<\/li>\n<li>Implementierungen der Randbedingung<\/li>\n<li>Dateneingabe-\/Ausgaberoutinen (Validierung, Formatierung)<\/li>\n<\/ul>\n<p>Diese Komponenten sind stabil, haben klare erwartete Verhaltensweisen und werden in vielen Simulationen wiederverwendet.<\/p>\n<h3>\u00dcbernehmen Sie pytest.approx als Standard<\/h3>\n<p>Verwenden Sie niemals <code>==<\/code> f\u00fcr Gleitkommaergebnisse. Verwenden Sie immer <code>pytest.approx()<\/code> mit entsprechenden Toleranzen. Machen Sie dies zu einer Teamkonvention.<\/p>\n<h3>Verwenden Sie Fixtures ausgiebig<\/h3>\n<p>Fixtures reduzieren die Duplizierung und machen Tests haltbarer. Erstellen Sie Fixtures f\u00fcr:<\/p>\n<ul>\n<li>Gemeinsame Netze (1D, 2D, 3D-Testgitter)<\/li>\n<li>Standard-Grenzbedingungen-Setups<\/li>\n<li>bekannte analytische L\u00f6sungen<\/li>\n<li>Tempor\u00e4re Datei- \/ Verzeichnisverwaltung<\/li>\n<\/ul>\n<h3>Integrieren Sie CI fr\u00fchzeitig<\/h3>\n<p>Richten Sie GitHub-Aktionen (oder \u00e4hnliches) ein, bevor das Projekt gro\u00df wird. Testen Sie automatisch Tests auf:<\/p>\n<ul>\n<li>Jeder Sto\u00df<\/li>\n<li>jede Pull-Anfrage<\/li>\n<li>Geplante n\u00e4chtliche Builds (um Umweltdrift zu fangen)<\/li>\n<\/ul>\n<h3>Code-Abdeckung messen und verfolgen<\/h3>\n<p>Verwenden Sie <code>pytest-cov<\/code>, um zu messen, welche Teile Ihres Codes durch Tests ausge\u00fcbt werden. Zielen Sie auf mindestens 80% der Kernmodule, aber nicht \u00fcber 100% besessen - das Ziel ist Vertrauen, keine perfekte Punktzahl.<\/p>\n<h3>Schreiben Sie Tests, wenn Sie Fehler beheben<\/h3>\n<p>Wenn ein Fehler gemeldet wird, schreiben Sie einen Test, der ihn reproduziert, bevor Sie es beheben. Dies garantiert, dass der Fehler sp\u00e4ter nicht mehr angezeigt wird.<\/p>\n<h3>Testen Sie schnell<\/h3>\n<p>Wenn ein Test l\u00e4nger als ein paar Sekunden dauert, beachten Sie:<\/p>\n<ul>\n<li>Verwenden kleinerer Testprobleme<\/li>\n<li>Verspotten teure Operationen<\/li>\n<li>Verschieben Sie es in eine weniger h\u00e4ufig ausgef\u00fchrte Integrationstest-Suite<\/li>\n<\/ul>\n<h2>Verwandte Anleitungen<\/h2>\n<ul>\n<li><a href=\"\/tracking-long-term-technical-debt-in-research-software\/\">Nachverfolgung langfristiger technischer Schulden in der Forschungssoftware<\/a> \u2013 Verwalten von Testschulden, wenn sich Projekte weiterentwickeln<\/li>\n<li><a href=\"\/managing-research-software-through-tickets\/\">Verwalten von Forschungssoftware \u00fcber Tickets<\/a> \u2013 Mithilfe von Issue-Tracking zur Koordinierung der Testbem\u00fchungen<\/li>\n<li><a href=\"\/reproducibility-and-its-role-in-debugging\/\">Reproduzierbarkeit und ihre Rolle beim Debuggen<\/a> \u2013 Wie Tests systematisches Debuggen erm\u00f6glichen<\/li>\n<li><a href=\"\/how-to-write-a-clear-and-useful-bug-report\/\">Wie schreibe ich einen klaren und n\u00fctzlichen Fehlerbericht<\/a> \u2013 Bereitstellung der Informationen, die zum Erstellen von Regressionstests erforderlich sind<\/li>\n<li><a href=\"\/feature-requests-vs-bug-reports-knowing-the-difference\/\">Funktionsanfragen vs. Fehlerberichte: Wissen \u00fcber den Unterschied<\/a> \u2013 Klassifizierung von Problemen, die die Testentwicklung vorantreiben<\/li>\n<\/ul>\n<h2>Schlussfolgerung<\/h2>\n<p>Unit Testing verwandelt Forschungssoftware aus fragilen Skripten in zuverl\u00e4ssige, reproduzierbare Instrumente. W\u00e4hrend das Einrichten umfassender Tests Vorabinvestitionen erfordert, kommt es zu einer k\u00fcrzeren Debugging-Zeit, einem h\u00f6heren Vertrauen in die Ergebnisse und einer reibungslosen Zusammenarbeit.<\/p>\n<p>Das PyTest-Framework bietet leistungsstarke Tools - Fixtures, Parametrisierung, Verspotten und <code>approx()<\/code>  -, die sich direkt mit den Herausforderungen des wissenschaftlichen Codes befassen: numerische Pr\u00e4zision, externe Abh\u00e4ngigkeiten und unbekannte richtige Antworten. In Kombination mit der kontinuierlichen Integration stellen diese Praktiken sicher, dass Tests konsistent \u00fcber Umgebungen hinweg ausgef\u00fchrt werden.<\/p>\n<p>Denken Sie daran: Beim Testen geht es nicht darum, Perfektion zu erreichen. Es geht darum, gen\u00fcgend Vertrauen in Ihren Code aufzubauen, dass Sie seinen Ausgaben vertrauen k\u00f6nnen, wenn es darauf ankommt. Beginnen Sie mit den Kernkomponenten, schreiben Sie Tests, die klare Erwartungen zum Ausdruck bringen, und erweitern Sie die Abdeckung schrittweise, wenn das Projekt w\u00e4chst.<\/p>\n<p>Ihr zuk\u00fcnftiges Selbst - und jeder, der Ihren Code erbt - wird es Ihnen danken.<\/p>\n<h2>N\u00e4chste Schritte<\/h2>\n<p>Sind Sie bereit, Ihrem Forschungsprojekt Tests hinzuzuf\u00fcgen?<\/p>\n<ol>\n<li>Installieren Sie Pytest: <code>pip install pytest<\/code><\/li>\n<li>Erstellen Sie ein <code>tests\/<\/code>-Verzeichnis mit einer einfachen Testdatei<\/li>\n<li>Schreiben Sie einen Test f\u00fcr eine Kernfunktion mit <code>pytest.approx<\/code><\/li>\n<li>Einrichten eines GitHub-Aktionen-Workflows, um Tests automatisch auszuf\u00fchren<\/li>\n<li>Erweitern Sie die Abdeckung schrittweise, w\u00e4hrend Sie den Code \u00e4ndern<\/li>\n<\/ol>\n<p>F\u00fcr die personalisierte Hilfe bei der Implementierung von Teststrategien in Ihrer spezifischen Forschungssoftware wenden Sie sich bitte an <a href=\"\/\"> f\u00fcr eine Beratung <\/a> (siehe unsere Homepage f\u00fcr weitere Informationen).<\/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\"> 10<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span>Unit-Tests sind f\u00fcr vertrauensw\u00fcrdige wissenschaftliche Software nicht verhandelbar. Im Gegensatz zu kommerziellen Anwendungen fehlen dem Forschungscode h\u00e4ufig formelle Tests, was zu nicht reproduzierbaren Ergebnissen und vergeudeten Anstrengungen f\u00fchrt. Dieser Leitfaden behandelt Pytest-Strategien speziell f\u00fcr wissenschaftliche Python-Projekte: Umgang mit numerischer Pr\u00e4zision mit pytest.approx, Isolierung externer Abh\u00e4ngigkeiten mit Spott, Verwendung von Fixtures und Parametrisierung effizient und Integration [&hellip;]<\/p>\n","protected":false,"raw":""},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_locale":"de_DE","_original_post":"https:\/\/matforge.org\/?p=187","iawp_total_views":0,"footnotes":""},"categories":[3],"tags":[],"class_list":["post-849","post","type-post","status-publish","format-standard","hentry","category-issue-tracking-tickets-technical-requests","de-DE"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Unit-Testing f\u00fcr wissenschaftlichen Code: PYTest-Strategien f\u00fcr Forschungsprojekte - matforge.org<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Unit-Testing f\u00fcr wissenschaftlichen Code: PYTest-Strategien f\u00fcr Forschungsprojekte - matforge.org\" \/>\n<meta property=\"og:description\" content=\"Reading Time:  10 minutesUnit-Tests sind f\u00fcr vertrauensw\u00fcrdige wissenschaftliche Software nicht verhandelbar. Im Gegensatz zu kommerziellen Anwendungen fehlen dem Forschungscode h\u00e4ufig formelle Tests, was zu nicht reproduzierbaren Ergebnissen und vergeudeten Anstrengungen f\u00fchrt. Dieser Leitfaden behandelt Pytest-Strategien speziell f\u00fcr wissenschaftliche Python-Projekte: Umgang mit numerischer Pr\u00e4zision mit pytest.approx, Isolierung externer Abh\u00e4ngigkeiten mit Spott, Verwendung von Fixtures und Parametrisierung effizient und Integration [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/\" \/>\n<meta property=\"og:site_name\" content=\"matforge.org\" \/>\n<meta property=\"article:published_time\" content=\"2026-07-30T12:22:25+00:00\" \/>\n<meta name=\"author\" content=\"Priya Nair\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Priya Nair\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"15\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/\"},\"author\":{\"name\":\"Priya Nair\",\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/2effd7bc155a5e6357f31dac970c5795\"},\"headline\":\"Unit-Testing f\u00fcr wissenschaftlichen Code: PYTest-Strategien f\u00fcr Forschungsprojekte\",\"datePublished\":\"2026-07-30T12:22:25+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/\"},\"wordCount\":2248,\"commentCount\":0,\"articleSection\":[\"Issue Tracking, Tickets & amp; Technische Anfragen\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/matforge.org\\\/de\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/\",\"url\":\"https:\\\/\\\/matforge.org\\\/de\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/\",\"name\":\"Unit-Testing f\u00fcr wissenschaftlichen Code: PYTest-Strategien f\u00fcr Forschungsprojekte - matforge.org\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/#website\"},\"datePublished\":\"2026-07-30T12:22:25+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/#\\\/schema\\\/person\\\/2effd7bc155a5e6357f31dac970c5795\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/matforge.org\\\/de\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/matforge.org\\\/de\\\/unit-testing-scientific-code-pytest-strategies-research-projects\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/matforge.org\\\/de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Unit-Testing f\u00fcr wissenschaftlichen Code: PYTest-Strategien f\u00fcr Forschungsprojekte\"}]},{\"@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\\\/2effd7bc155a5e6357f31dac970c5795\",\"name\":\"Priya Nair\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g\",\"caption\":\"Priya Nair\"},\"sameAs\":[\"http:\\\/\\\/matforge.org\"],\"url\":\"https:\\\/\\\/matforge.org\\\/author\\\/priya-nair\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Unit-Testing f\u00fcr wissenschaftlichen Code: PYTest-Strategien f\u00fcr Forschungsprojekte - matforge.org","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/","og_locale":"de_DE","og_type":"article","og_title":"Unit-Testing f\u00fcr wissenschaftlichen Code: PYTest-Strategien f\u00fcr Forschungsprojekte - matforge.org","og_description":"Reading Time:  10 minutesUnit-Tests sind f\u00fcr vertrauensw\u00fcrdige wissenschaftliche Software nicht verhandelbar. Im Gegensatz zu kommerziellen Anwendungen fehlen dem Forschungscode h\u00e4ufig formelle Tests, was zu nicht reproduzierbaren Ergebnissen und vergeudeten Anstrengungen f\u00fchrt. Dieser Leitfaden behandelt Pytest-Strategien speziell f\u00fcr wissenschaftliche Python-Projekte: Umgang mit numerischer Pr\u00e4zision mit pytest.approx, Isolierung externer Abh\u00e4ngigkeiten mit Spott, Verwendung von Fixtures und Parametrisierung effizient und Integration [&hellip;]","og_url":"https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/","og_site_name":"matforge.org","article_published_time":"2026-07-30T12:22:25+00:00","author":"Priya Nair","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Priya Nair","Gesch\u00e4tzte Lesezeit":"15\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/#article","isPartOf":{"@id":"https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/"},"author":{"name":"Priya Nair","@id":"https:\/\/matforge.org\/#\/schema\/person\/2effd7bc155a5e6357f31dac970c5795"},"headline":"Unit-Testing f\u00fcr wissenschaftlichen Code: PYTest-Strategien f\u00fcr Forschungsprojekte","datePublished":"2026-07-30T12:22:25+00:00","mainEntityOfPage":{"@id":"https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/"},"wordCount":2248,"commentCount":0,"articleSection":["Issue Tracking, Tickets & amp; Technische Anfragen"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/","url":"https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/","name":"Unit-Testing f\u00fcr wissenschaftlichen Code: PYTest-Strategien f\u00fcr Forschungsprojekte - matforge.org","isPartOf":{"@id":"https:\/\/matforge.org\/#website"},"datePublished":"2026-07-30T12:22:25+00:00","author":{"@id":"https:\/\/matforge.org\/#\/schema\/person\/2effd7bc155a5e6357f31dac970c5795"},"breadcrumb":{"@id":"https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/matforge.org\/de\/unit-testing-scientific-code-pytest-strategies-research-projects\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/matforge.org\/de\/"},{"@type":"ListItem","position":2,"name":"Unit-Testing f\u00fcr wissenschaftlichen Code: PYTest-Strategien f\u00fcr Forschungsprojekte"}]},{"@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\/2effd7bc155a5e6357f31dac970c5795","name":"Priya Nair","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f11e168d4cd2f1eff83cbb851d6cff42d81d88bb59d8831ee77468aa4a5eea88?s=96&d=mm&r=g","caption":"Priya Nair"},"sameAs":["http:\/\/matforge.org"],"url":"https:\/\/matforge.org\/author\/priya-nair\/"}]}},"_links":{"self":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/849","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/comments?post=849"}],"version-history":[{"count":1,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/849\/revisions"}],"predecessor-version":[{"id":959,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/posts\/849\/revisions\/959"}],"wp:attachment":[{"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/media?parent=849"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/categories?post=849"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/matforge.org\/wp-json\/wp\/v2\/tags?post=849"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}