{"id":4751,"date":"2019-08-07T16:35:12","date_gmt":"2019-08-07T14:35:12","guid":{"rendered":"http:\/\/blog.via-internet.de\/?p=4751"},"modified":"2022-04-27T18:50:35","modified_gmt":"2022-04-27T16:50:35","slug":"part-1-tdd-with-python","status":"publish","type":"post","link":"https:\/\/via-internet.de\/blog\/2019\/08\/07\/part-1-tdd-with-python\/","title":{"rendered":"Python | Test-Driven Development"},"content":{"rendered":"\n\n\n<ul class=\"wp-block-list\"><li>Part 1: Create a TDD Python Project<\/li><li>Part 2: Use Jenkins to automatically test your App<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Part 1: Create a TDD Python Project<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Final source code is on&nbsp;<a href=\"https:\/\/github.com\/fullstack-toolbox\/Testproject_Python-Calculator\">Github<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The task of creating an error free program is not easy. And, if your program runs free of errors, keeping it error-free after an update or change is even more complicated. You don&#8217;t want to insert new errors or change correct code with wrong parts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The answer to this situation (directly from the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Delphi#Oracle_of_Delphi\">Oracle of Delphi<\/a>) is: <strong>Testing, Testing, Testing<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And the best way to test is to <strong>start with tests<\/strong>. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This means: think about <em>what the result should<\/em> be and then create a Test <em>that checks this<\/em>.  Imagine, you have to write a function for adding two values, and you should describe the functionality. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So, maybe, your description contains one or two examples:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"has-text-align-left wp-block-paragraph\">My functions add&#8217;s two numbers, e.g 5 plus 7 is 12 (or at least should be 12 :))<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The procedure with the TDD is:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>think and define, what the function should to<\/li><li>write a stub for the function, e.g. only function parameters and return type<\/li><li>write a function, that tests you function with defines parameters and know result<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For our example above, this means:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Write the python script with the desired functionality: <code><code>src\/main.py<\/code><\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"1\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def add(val1,val2):\n    return 0 # this is only a dummy return value<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Write the Python Testscript: <code><code><code>tst\/main.p<\/code><\/code><\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"1\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def_test_add():\n    result = add(5,7)\n\n    if (result = 12):\n        print(\"everything fine\")\n    else:\n        printf(\"ups, problems with base arithmetics\")<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now, with these in your toolbox, you can always verify your code by running the tests.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"1\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ python test_add.py\nups, problems with base arithmetics<\/pre>\n\n\n\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<p class=\"wp-block-paragraph\">dfdf<\/p>\n<\/div><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Setup virtual environment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Mostly, tests are repeated after every change. So, to be sure, that each test is running the same way and with the same environment, we will use pythons virtual environment feature to create a new fresh python environment for the tests.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Create virtual environment<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ python3 -m venv .env\/python<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Activate environment<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add the following line to .bashrc (or .envrc if you are using direnv)<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ . .env\/python\/bin\/activate<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Install required packages<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ pip install pytest<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Create a sample Application<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Prepare folder<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Create folder for sources<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ mkdir src<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create sample package<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ mkdir src\/CalculatorLib\n$ touch src\/CalculatorLib\/__init__.py\n$ touch src\/CalculatorLib\/Calculator.py<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">At least, create a simple Calculator: <code>src\/CalculatorLib\/Calculator.py<\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class Calculator:\n    def __init__(self):\n        print(\"Init Calculator\")\n\n    def add(self, a, b):\n        return a + b\n\n    def subtract(self, a, b):\n        return a - b\n\n    def multiply(self, a, b):\n        return a * b\n\n    def divide(self, a, b):\n        return a \/ b\n\n    def power(self, base, exp):\n        return base ** exp<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create the Main App for your Calculator: <code><code>src\/main.py<\/code><\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">from CalculatorLib.Calculator import Calculator\n\nclass Main(object):\n\n    def run(self):\n        c = Calculator()\n\n        print(\"5 + 3 = \n        print(\"8 - 4 = \n        print(\"5 * 3 = \n        print(\"8 \/ 4 = \n\n        print(\"8 ^ 4 = \n\nif __name__ == '__main__':\n    Main().run()<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Yur done with the fist development step. Try your app:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ python src\/main.py\nInit Calculator\n5 + 3 =     8\n8 - 4 =     4\n5 * 3 =    15\n8 \/ 4 =     2\n8 ^ 4 =  4096<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Add Unit Tests<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">We will start with our first test. Create folder for tests and a file <code>tst\/main.py<\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ mkdir tst\n$ touch tst\/main.py<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use the following for your test script  <code>tst\/main.py<\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"1,4\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">from CalculatorLib.Calculator import Calculator\nimport unittest\n\nclass CalculatorTest(unittest.TestCase):\n\n    @classmethod\n    def setUpClass(self):\n        self.c = Calculator()\n\n    def test_add(self):\n        self.assertEqual(8, self.c.add(5, 3))\n\n    def test_subtract(self):\n        self.assertEqual(4, self.c.subtract(8, 4))\n\n    def test_multiply(self):\n        self.assertEqual(32, self.c.multiply(8, 4))\n\n    def test_divide(self):\n        self.assertEqual(2, self.c.divide(8, 4))\n            \n    def test_power(self):\n        self.assertEqual(16, self.c.power(2, 4))\n                                    \nif __name__ == '__main__':\n    unittest.main()<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Finally try your test script:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ PYTHONPATH=.\/src python -m pytest tst\/main.py  --verbose\n================================= test session starts ================================\nplatform darwin -- Python 3.7.4, pytest-4.4.1, py-1.8.0, pluggy-0.9.0 -- &lt;Testproject_Python-Calculator\/.env\/python\/bin\/python>\ncachedir: .pytest_cache\nrootdir: &lt;Testproject_Python-Calculator>\nplugins: cov-2.6.1\ncollected 5 items\n\ntst\/main.py::CalculatorTest::test_add PASSED             [ 20\ntst\/main.py::CalculatorTest::test_divide PASSED          [ 40\ntst\/main.py::CalculatorTest::test_multiply PASSED        [ 60\ntst\/main.py::CalculatorTest::test_power PASSED           [ 80\ntst\/main.py::CalculatorTest::test_subtract PASSED        [100\n\n\n\n<p class=\"wp-block-paragraph\">The command to run the test is <code>python -m pytest tst\/main.py,<\/code> but why the lead Variable <code>PYTHONPATH<\/code>?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Try it without:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ python -m pytest tst\/main.py\n=================================== test session starts ==================================\nplatform darwin -- Python 3.7.4, pytest-4.4.1, py-1.8.0, pluggy-0.9.0 -- ##\/Testproject_Python-Calculator\/.env\/python\/bin\/python\ncachedir: .pytest_cache\nrootdir: ##\/Testproject_Python-Calculator\nplugins: cov-2.6.1\ncollected 0 items \/ 1 errors\n\n========================================= ERRORS =========================================\n____________________________________ ERROR collecting tst\/main.py ________________________\nImportError while importing test module '##\/Testproject_Python-Calculator\/tst\/main.py'.\nHint: make sure your test modules\/packages have valid Python names.\nTraceback:\ntst\/main.py:2: in &lt;module>\n    from CalculatorLib.Calculator import Calculator\nE   ModuleNotFoundError: No module named 'CalculatorLib'\n!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!!!!!\n================================== 1 error in 1.84 secon==================================\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Recognize the ModuleNotFoundError in line 16! This means, that Python could not find the desired CalculatorLib.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Look at your folder structure:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ tree .\n.\n\u251c\u2500\u2500 src\n\u2502   \u251c\u2500\u2500 CalculatorLib\n\u2502   \u2502   \u251c\u2500\u2500 Calculator.py\n\u2502   \u2502   \u251c\u2500\u2500 init__.py\n\u2502   \u2514\u2500\u2500 main.py\n\u2514\u2500\u2500 tst\n    \u2514\u2500\u2500 main.py<\/pre>\n\n\n\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<p class=\"wp-block-paragraph\">.<\/p>\n<\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">In your Testscript, we import the CalculatorLib whit this statement:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">from CalculatorLib.Calculator import Calculator<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Python is interpreting this in the following way:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Look in the folder of the test script for a subfolder with the name CalculatorLib<\/li><li>There, look for a file&nbsp;<code>Calculator.py<\/code><\/li><li>And in this file, use the class Calculator<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Obviously, the folder&nbsp;<code>CalculatorLib<\/code>&nbsp;is NOT in the same folder as the test script: it is part of the&nbsp;<code>src<\/code>&nbsp;folder.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So, using the environment variable&nbsp;<code>PYTHONPATH<\/code>, we inform python where to search python scripts and folders.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Add additional functionality<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Add a function at the end of your Calculator:&nbsp;<code>src\/CalculatorLib\/Calculator.py<\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">    ....\n    def factorial(self, n):\n        return 0<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add a call of the new function to your main app:&nbsp;<code>src\/main.py<\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"4\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">    ...\n    def run(self):\n        ...\n        print(\"4!    = \n\n\n\n<p class=\"wp-block-paragraph\">Add a test for the new function to your test script:&nbsp;<code>tst\/main.py<\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"3\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">    ...\n    def test_factorial(self):\n        self.assertEqual(24, self.c.factorial(4))<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Try it:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"1\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ python src\/main.py\nInit Calculator\n5 + 3 =     8\n8 - 4 =     4\n5 * 3 =    15\n8 \/ 4 =     2\n8 ^ 4 =  4096<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"1\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"16\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ PYTHONPATH=.\/src python -m pytest tst\/main.py\n==================================== test session starts =====================================\nplatform darwin -- Python 3.7.4, pytest-4.4.1, py-1.8.0, pluggy-0.9.0\nrootdir: ##\/Testproject_Python-Calculator\nplugins: cov-2.6.1\ncollected 6 items\n\ntst\/main.py ..F...                                                                      [100\n\n========================================== FAILURES ==========================================\n_______________________________ CalculatorTest.test_factorial ________________________________\n\nself = &lt;main.CalculatorTest testMethod=test_factorial>\n\n    def test_factorial(self):\n>       self.assertEqual(24, self.c.factorial(4))\nE       AssertionError: 24 != 0\n\ntst\/main.py:31: AssertionError\n============================= 1 failed, 5 passed in 0.14 seconds =============================<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Test failed, was we expect it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now, implement the function correctly and startover the test:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Add a function at the end of your Calculator:&nbsp;<code>src\/CalculatorLib\/Calculator.py<\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import math\n\nclass Calculator:\n    ...\n    def factorial(self, n):\n       if not n >= 0:\n            raise ValueError(\"n must be >= 0\")\n\n        if math.floor(n) != n:\n            raise ValueError(\"n must be exact integer\")\n\n        if n+1 == n:  # catch a value like 1e300\n            raise OverflowError(\"n too large\")\n\n        result, factor = 1, 2\n        \n        while factor &lt;= n:\n            result *= factor\n            factor += 1\n\n        return result<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ PYTHONPATH=.\/src python -m pytest tst\/main.py  --verbose\n==================================== test session starts =====================================\nplatform darwin -- Python 3.7.4, pytest-4.4.1, py-1.8.0, pluggy-0.9.0 -- ##\/Testproject_Python-Calculator\/.env\/python\/bin\/python\ncachedir: .pytest_cache\nrootdir: ##\/Testproject_Python-Calculator\nplugins: cov-2.6.1\ncollected 6 items\n\ntst\/main.py::CalculatorTest::test_add PASSED                                             [ 16\ntst\/main.py::CalculatorTest::test_divide PASSED                                          [ 33\ntst\/main.py::CalculatorTest::test_factorial PASSED                                       [ 50\ntst\/main.py::CalculatorTest::test_multiply PASSED                                        [ 66\ntst\/main.py::CalculatorTest::test_power PASSED                                           [ 83\ntst\/main.py::CalculatorTest::test_subtract PASSED                                        [100\n\n================================== 6 passed in 0.01 seconds ==================================<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Testing Frameworks<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/wiki.python.org\/moin\/PythonTestingToolsTaxonomy\">https:\/\/wiki.python.org\/moin\/PythonTestingToolsTaxonomy<\/a>            <\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/docs.python.org\/2\/library\/unittest.html\">Unit testing framework<\/a><\/h4>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import unittest\n\nclass TestStringMethods(unittest.TestCase):\n\n    def test_upper(self):\n        self.assertEqual('foo'.upper(), 'FOO')\n\n    def test_isupper(self):\n        self.assertTrue('FOO'.isupper())\n        self.assertFalse('Foo'.isupper())\n\n    def test_split(self):\n        s = 'hello world'\n        self.assertEqual(s.split(), ['hello', 'world'])\n        \n        with self.assertRaises(TypeError):\n            s.split(2)\n\nif __name__ == '__main__':\n    unittest.main()<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/docs.pytest.org\/en\/latest\/\">pytest &#8211; helps you write better programms<\/a><\/h4>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># content of test_sample.py\ndef inc(x):\n    return x + 1\n\ndef test_answer():\n    assert inc(3) == 5<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ pytest<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/nose.readthedocs.io\/en\/latest\/\">nose &#8211; is nicer testing for python<\/a><\/h4>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def test_numbers_3_4():\n    assert multiply(3,4) == 12 \n \ndef test_strings_a_3():\n    assert multiply('a',3) == 'aaa<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/github.com\/legshort\/apple-mango\">Python BDD Pattern<\/a><\/h4>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class MangoUseCase(TestCase):\n  def setUp(self):\n    self.user = 'placeholder'\n\n  @mango.given('I am logged-in')\n  def test_profile(self):\n    self.given.profile = 'profile'\n    self.given.photo = 'photo'\n\n    self.given.notifications = 3\n    self.given.notifications_unread = 1\n\n    @mango.when('I click profile')\n    def when_click_profile():\n      print('click')\n\n      @mango.then('I see profile')\n      def then_profile():\n        self.assertEqual(self.given.profile, 'profile')\n\n      @mango.then('I see my photo')\n        def then_photo():\n          self.assertEqual(self.given.photo, 'photo')<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"http:\/\/radish-bdd.io\/\">radsh is not just another BDD tool &#8230;THE ROOT FROM RED TO GREEN<\/a><\/h4>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">from radish import given, when, then\n\n@given(\"I have the numbers {number1:g} and {number2:g}\")\ndef have_numbers(step, number1, number2):\n    step.context.number1 = number1\n    step.context.number2 = number2\n\n@when(\"I sum them\")\ndef sum_numbers(step):\n    step.context.result = step.context.number1 + \\\n        step.context.number2\n\n@then(\"I expect the result to be {result:g}\")\ndef expect_result(step, result):\n    assert step.context.result == result<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/docs.python.org\/3.7\/library\/doctest.html\">doctest<\/a><\/h4>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\"\"\"\nThe example module supplies one function, factorial().  For example,\n\n>>> factorial(5)\n120\n\"\"\"\n\ndef factorial(n):\n    \"\"\"Return the factorial of n, an exact integer >= 0.\n\n    >>> [factorial(n) for n in range(6)]\n    [1, 1, 2, 6, 24, 120]\n    >>> factorial(30)\n    265252859812191058636308480000000\n    >>> factorial(-1)\n    Traceback (most recent call last):\n        ...\n    ValueError: n must be >= 0\n\n    Factorials of floats are OK, but the float must be an exact integer:\n    >>> factorial(30.1)\n    Traceback (most recent call last):\n        ...\n    ValueError: n must be exact integer\n    >>> factorial(30.0)\n    265252859812191058636308480000000\n\n    It must also not be ridiculously large:\n    >>> factorial(1e100)\n    Traceback (most recent call last):\n        ...\n    OverflowError: n too large\n    \"\"\"\n\n    import math\n    if not n >= 0:\n        raise ValueError(\"n must be >= 0\")\n    if math.floor(n) != n:\n        raise ValueError(\"n must be exact integer\")\n    if n+1 == n:  # catch a value like 1e300\n        raise OverflowError(\"n too large\")\n    result = 1\n    factor = 2\n    while factor &lt;= n:\n        result *= factor\n        factor += 1\n    return result\n\nif __name__ == \"__main__\":\n    import doctest\n    doctest.testmod()<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Sample Session with Test Frameworks<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"1\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ py.test -v\n========================================================= test session starts ==========================================================\nplatform darwin -- Python 3.7.3, pytest-4.3.1, py-1.8.0, pluggy-0.9.0 -- \/CLOUD\/Development.Anaconda\/anaconda3\/bin\/python\ncachedir: .pytest_cache\nrootdir: \/CLOUD\/Development.Python\/Repositories.FromGithub\/repositories\/python-toolbox\/Working-with-TDD\/app, inifile:\nplugins: remotedata-0.3.1, openfiles-0.3.2, doctestplus-0.3.0, arraydiff-0.3\ncollected 4 items\n\ntest_base.py::test_should_pass PASSED                                                                                            [ 25\ntest_base.py::test_should_raise_error PASSED                                                                                     [ 50\ntest_base.py::test_check_if_true_is_true PASSED                                                                                  [ 75\ntest_base.py::test_check_if_inc_works PASSED<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"1\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ nosetests -v\ntest_base.test_should_pass ... ok\ntest_base.test_should_raise_error ... ok\ntest_base.test_check_if_true_is_true ... ok\ntest_base.test_check_if_inc_works ... ok\n\n----------------------------------------------------------------------\nRan 4 tests in 0.001s\n\nOK<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Links and additional information<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"http:\/\/pythontesting.net\/\">http:\/\/pythontesting.net\/<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/www.xenonstack.com\/blog\/test-driven-development-big-data\">https:\/\/www.xenonstack.com\/blog\/test-driven-development-big-data<\/a>\/<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/realpython.com\/python-testing\/\">https:\/\/realpython.com\/python-testing\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Part 1: Create a TDD Python Project Part 2: Use Jenkins to automatically test your App Part 1: Create a TDD Python Project Final source code is on&nbsp;Github. Introduction The task of creating an error free program is not easy. And, if your program runs free of errors, keeping it error-free after an update or change is even more complicated. You don&#8217;t want to insert new errors or change correct code with wrong parts. The answer to this situation (directly from the Oracle of Delphi) is: Testing, Testing, Testing And the best way to test is to start with tests. This [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":5047,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[65],"tags":[],"class_list":["post-4751","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python"],"jetpack_featured_media_url":"https:\/\/via-internet.de\/blog\/wp-content\/uploads\/2019\/08\/Bildschirmfoto-2019-08-07-um-19.40.07.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/posts\/4751","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/comments?post=4751"}],"version-history":[{"count":1,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/posts\/4751\/revisions"}],"predecessor-version":[{"id":8819,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/posts\/4751\/revisions\/8819"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/media\/5047"}],"wp:attachment":[{"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/media?parent=4751"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/categories?post=4751"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/tags?post=4751"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}