{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Qualitätskontrolle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ist ein Programm erstmal geschrieben, vergisst man schnell die Details,\n", "es ändern sich im Laufe der Zeit die darunterligenden Bibliotheken\n", "und natürlich die Anwendungsfälle.\n", "Besonders schwierig wird es dann,\n", "später Fehler auszubessern oder bei nachträglichen Änderungen nicht neue Fehler einzubauen.\n", "\n", "Ein wichtiges Hilfsmittel hierfür sind Tests zur Qualitätskontrolle.\n", "Dabei handelt es sich um kleine Aufrufe der einzelnen Funktionen bzw. Methoden des Programms,\n", "wobei die jeweiligen Rückgabewerte mit den zu erwartenden Ergebnissen verglichen werden.\n", "Damit alle Tests passen, müssen alle Rückgaben exakt die zu erwartenden sein!\n", "\n", "Ein Programm sollte auch möglichst vollständig durch solche Testaufrufe abgedeckt werden.\n", "Das nennt sich [coverage](https://en.wikipedia.org/wiki/Code_coverage).\n", "\n", "Siehe auch: [Python Tutorial/Qualitätskontrolle](https://docs.python.org/2/tutorial/stdlib.html#quality-control)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Doctesting\n", "\n", "In die Doumentationskommentare der Funktionen und Methoden werden\n", "eingerückt durch `>>>` Anweisungen eingebaut und ausgeführt.\n", "Darunter steht jeweils das zu erwartende Ergebnis.\n", "\n", "Gibt es einen Unterschied, meldet dies der Test." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from __future__ import division" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Berechnung des Mittelwerts mit einem Doctest:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def average(numbers):\n", " \"\"\"\n", " Durchschnitt der übergebenen Liste von Zahlen\n", " \n", " >>> average([2, 10, 12])\n", " 8.0\n", "\n", " >>> x = average([7,7,7,7,7,7,7,7,7,7,7,8])\n", " >>> 7.05 < x < 7.1\n", " True\n", " \"\"\"\n", " return sum(numbers) / len(numbers)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "TestResults(failed=0, attempted=3)" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import doctest\n", "doctest.testmod()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Berechnung des Medians, wobei leider im Algorithmus ein wichtiger Schritt vergessen wurde.\n", "Daher passt dieser Test nicht!" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def wrong_median(numbers):\n", " \"\"\"\n", " Median der übergebenen Liste von Zahlen\n", " \n", " >>> wrong_median([5, 10, 1, 10, 2])\n", " 5.0\n", " \"\"\"\n", " i = len(numbers) // 2\n", " if len(numbers) % 2 == 0:\n", " return (numbers[i] + numbers[i+1]) / 2.\n", " return numbers[i]" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "**********************************************************************\n", "File \"__main__\", line 5, in __main__.wrong_median\n", "Failed example:\n", " wrong_median([5, 10, 1, 10, 2])\n", "Expected:\n", " 5.0\n", "Got:\n", " 1\n", "**********************************************************************\n", "1 items had failures:\n", " 1 of 1 in __main__.wrong_median\n", "***Test Failed*** 1 failures.\n" ] }, { "data": { "text/plain": [ "TestResults(failed=1, attempted=4)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "doctest.testmod()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Unittests\n", "\n", "Allgemeiner als Doctests sind **Unittests**.\n", "Sie sind üblicherweise Teil eines komplexeren Frameworks,\n", "um Teile des gesamten Programms zu testen.\n", "Es gibt u.A. `setUp` und `tearDown` Routinen,\n", "um vor und nach der Ausführen der eigentlichen Tests den Programmstatus passend einzustellen\n", "(z.B. Datenbankverbindung herstellen, bevor Datenbankfunktionstests ausgeführt werden)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Im folgenden berechnet die Funktion `steuern(einkommen)` den progressiven Steuersatz für das Einkommen.\n", "Die `ramp(...)` Funktion ist hierbei hilfreich, um sich langwierige Fallunterscheidungen und Zwischenrechnungen zu ersparen.\n", "\n", "$$\\operatorname{ramp}(x) := \\begin{cases}\n", "x & x \\geq 0 \\\\\n", "0 & \\text{otherwise}\n", "\\end{cases}$$" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# ramp function (x * heavyside)\n", "ramp = lambda x : x if x >= 0. else 0.\n", "\n", "def steuern(einkommen):\n", " s = 0.\n", " s += 0.25 * ramp(min(20000, einkommen) - 11000)\n", " s += 0.35 * ramp(min(30000, einkommen) - 20000)\n", " s += 0.42 * ramp(min(60000, einkommen) - 30000)\n", " s += 0.48 * ramp(min(90000, einkommen) - 60000)\n", " s += 0.50 * ramp(min(1000000, einkommen) - 90000)\n", " s += 0.55 * ramp(einkommen - 1000000)\n", " return s " ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.0" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "steuern(1000)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.0" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "steuern(10500)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "25.0" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "steuern(11100)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "4003.5" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "steuern(25010)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "7850.0" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "steuern(35000)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "37750.0" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "steuern(100000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hier nun die Definition von drei Tests -- als Teil eines `unittest.TestCase`s --\n", "wobei einer fehlschlägt.\n", "\n", "Man beachte, in der ersten Zeile der Ausgabe stehen die Punkte für die erfolgreich ausgeführten Tests,\n", "während der dritte mit einem \"F\" einen Fehler signalisiert." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import unittest" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class TestSteuern(unittest.TestCase):\n", " def test_1(self):\n", " assert steuern(1000) == 0.\n", " def test_2(self):\n", " assert steuern(25000) < 5000\n", " def test_3(self):\n", " assert(steuern(1000000) / 1000000) > .5" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "..F\n", "======================================================================\n", "FAIL: test_3 (__main__.TestSteuern)\n", "----------------------------------------------------------------------\n", "Traceback (most recent call last):\n", " File \"\", line 7, in test_3\n", " assert(steuern(1000000) / 1000000) > .5\n", "AssertionError\n", "\n", "----------------------------------------------------------------------\n", "Ran 3 tests in 0.004s\n", "\n", "FAILED (failures=1)\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "suite = unittest.TestLoader().loadTestsFromTestCase(TestSteuern)\n", "unittest.TextTestRunner().run(suite)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Der vollständigkeithalber, ein Plot für Einkommen von 0 bis 200000€." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\nmVWAefNgyhSYOhWmTVtx6dABdtwRdtghXfR32GH5+qabgnIYR+mEYGa2Fj7+OLXhT56clilTli+L\nF0OPHunC36PH8vUdd4QuXfKO/JOcEMzMWmHx4nSRf+EFmDRp+TJjRurE7dkTdtppxWXLLfP5pb+m\nnBDMzAosXpx+6U+cuHx54YXUmbvDDrDzzmnp2RM+97n0q39lo3baGicEM6tJDQ3p1/3zz8OECen1\nv/9NY/a33x569YJddlm+bL89rLNO3lGXlhOCmVW9+fPhuefShb/xdeLE1Hm7665p6dUrvfboAR07\n5h1xPpwQzKxqRMCcOTB2LIwfD+PGpdc33kgX/N12S8vnP58u/p075x1xZXFCMLM2KSLdiuHZZ9My\ndmxali6F3r1hjz3Ssvvuqe2/ffu8I658TghmVvHmz1+xrb+xvf/Tn4Y990xL797ptWvXtjWyp5I4\nIZhZxVi6NI3pHz8+tfU3Lm+9tbyNv7DNf9NN8464ujghmFkuPv44jeNvbOoZNy5d/Dt1Sk09je39\nu+2WRvi085PZS84JwcxKrvHiP2bM8jb/559Pd94sbO/fYw/YeOO8o61dTghmVlQRaXz/6NEpAYwe\nnZqAtt46tfHvtVdadt8dNtww72itkBOCma2VBQvShf+pp2DUKHj66TSOv08f2Gef9Lrnnh7i2RY4\nIZhZq0WkTt///AeefDIt06enpp4vfCElgH33TSN9rO1xQjCzlWpoSEM9//1veOKJtADsv39a9tsv\nNf3U6szeauOEYGbLfPxxGu1TXw+PPZZqAptvDl/8IhxwABx4YHpKl8f5VycnBLMatnRpqgE88kha\nnngijfypq4ODDkqJYLPN8o7SysUJwazGvPQSPPQQPPxwSgIbbwx9+8LBB6dE4MletcsJwazKvfde\nuvA/+CCMHAkLF6aHsn/pSykRbL113hFapXBCMKsyEenWzvfdB/ffD888k0b+HH44HHZYuu2D+wCs\nOU4IZlVg8WJ49FH45z/T0r49HHkkHHFEagpaf/28I7S2oFwJoUOpT2BWa958M138R4xITUK77QZH\nHw0PPJAe7ehagFUq1xDMimDmTLjrrrSMH5/6AY49NtUGfA8gW1tuMjKrcNOnw+23p+Wll+CYY+C4\n41LHcLU83N0qgxOCWQWaORNuuQVuvRVmzYKvfhWOPz7NDejgBlgrEScEswrx5pspCQwbBlOmpCTQ\n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\nTkSuQmCC\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from __future__ import division\n", "xx = range(1, 120000, 1000)\n", "yy = [steuern(s)/s for s in xx]\n", "plt.title(\"Grenzsteuersatz\")\n", "plt.ylim(0, 0.6)\n", "plt.hlines(0.55, 0, 120000, linestyles=\"dotted\")\n", "plt.plot(xx, yy)" ] } ], "metadata": { "kernelspec": { "display_name": "Anaconda (Python 3)", "language": "python", "name": "anaconda3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 }