{"id":127,"date":"2015-06-13T13:47:27","date_gmt":"2015-06-13T13:47:27","guid":{"rendered":"http:\/\/www.arxcruz.net\/?p=127"},"modified":"2015-06-13T13:47:27","modified_gmt":"2015-06-13T13:47:27","slug":"writing-tests-in-python-for-beginners-part-1-doctest","status":"publish","type":"post","link":"https:\/\/arxcruz.net\/index.php\/2015\/06\/13\/writing-tests-in-python-for-beginners-part-1-doctest\/","title":{"rendered":"Writing tests in python for beginners &#8211; Part 1 &#8211; doctest"},"content":{"rendered":"<p>So, I just started an interest on python tests and I would like to share what I learned so far.<\/p>\n<p>Let&#8217;s skip all that theory about how tests are important for the code, bla bla bla, if you want to learn test, probably you already have your own reason, so, let&#8217;s jump to the part you actually write code!<\/p>\n<h5>Our application<\/h5>\n<p>Here a\u00a0 simple code that calculates fibonacci. Let&#8217;s save it in the file fibonacci.py<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\ndef fibonacci(number):\n    if number &lt; 2:\n        return number\n    return fibonacci(number - 1) + fibonacci(number - 2)\n\n<\/pre>\n<p>So, as you can see, it&#8217;s a very simple code that calculates the fibonacci number, so, how can we ensure that this is working? Well, the easiest way is just invoke the code with different values, and check if the result will be the expected. So, in other words, you&#8217;re doing exactly what doctest intent to do!<br \/>\nso, probably you would write a python file, let&#8217;s say, test_fibonacci.py with the following content:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\nfrom fibonacci import fibonacci\n\ndef test_fibonacci():\n    expected_result = [0, 1, 1, 2, 3, 5]\n    for x in range(0, 5):\n        result = fibonacci(x)\n        if result != expected_result[x]:\n            print &quot;Failing on calculate fibonacci %s&quot; % x\n\nif __name__ == '__main__':\n    print &quot;Testing fibonacci&quot;\n    test_fibonacci()\n\n<\/pre>\n<p>Well, it might work, until you want to calculate fibonacci(200), then you gonna need to add a new list, alter the test code, etc.<br \/>\nWhat if we now only accept positive numbers and negative numberes throws an exception? Try\/catch block? Well, soon you&#8217;re writing more code, and adding more complexity than you were expecting. Maybe you gonna need to test your test code itself to ensure that it&#8217;s working fine (are you feeling the inception here?)<\/p>\n<h5>Doctest<\/h5>\n<p>So, doctest can help you with some of these problems! Let&#8217;s see, first of all, you&#8217;re gonna need to create a simple txt file and add the following content:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&gt;&gt;&gt; from fibonacci import fibonacci\n\n&gt;&gt;&gt; fibonacci(0)\n0\n\n&gt;&gt;&gt; fibonacci(1)\n1\n\n&gt;&gt;&gt; fibonacci(2)\n1\n\n&gt;&gt;&gt; fibonacci(3)\n2\n\n&gt;&gt;&gt; fibonacci(4)\n3\n\n&gt;&gt;&gt; fibonacci(7)\n12\n<\/pre>\n<p>So, basically, doctest expect every line starting with &#8220;&gt;&gt;&gt;&#8221; to be a python code (we are importing fibonacci function in the line number 1 for example. And any other line as the result, looking just like the python shell.<\/p>\n<p>Assuming that both fibonacci.py and fibonacci.txt are in the same directory, you can run the doctest with the command:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n$ python -m doctest fibonacci.txt\n**********************************************************************\nFile &quot;fibonacci.txt&quot;, line 18, in fibonacci.txt\nFailed example:\n    fibonacci(7)\nExpected:\n    12\nGot:\n    13\n**********************************************************************\n1 items had failures:\n   1 of   7 in fibonacci.txt\n***Test Failed*** 1 failures.\n<\/pre>\n<p>Oops! Something went wrong! Don&#8217;t worry, that was expected. As you can see, the doctest shows a very good output when some error occurs, it shows you the line where the error happened, what was the expected result, and what was the result doctest gots. So, after fixing it, and runing again, we got no error<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&gt;&gt;&gt; from fibonacci import fibonacci\n\n&gt;&gt;&gt; fibonacci(0)\n0\n\n&gt;&gt;&gt; fibonacci(1)\n1\n\n&gt;&gt;&gt; fibonacci(2)\n1\n\n&gt;&gt;&gt; fibonacci(3)\n2\n\n&gt;&gt;&gt; fibonacci(4)\n3\n\n&gt;&gt;&gt; fibonacci(7)\n13\n<\/pre>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n$ python -m doctest fibonacci.txt\n<\/pre>\n<p>Now let&#8217;s go further, and say: Hey, I don&#8217;t want negative numbers to be allowed, I want it to throw an exception. So, in this case, we just need to add a new like in our fibonacci.txt testing the fibonacci(-1) and of course, checking if the result will be an exception.<br \/>\nIf we would do that in our test_fibonacci.py code, we should use a try\/catch block, call the fibonacci(-1) inside it, and of course, this woudln&#8217;t be in the initial loop, and if something goes wrong, you wouldn&#8217;t have the cool output from doctest.<\/p>\n<p>So, let&#8217;s change our fibonacci.py to not accept negative numbers:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\ndef fibonacci(number):\n    if number == 1 or number == 0:\n        return number\n    if number &lt; 0:\n        raise ValueError('Number is negative')\n    return fibonacci(number - 1) + fibonacci(number - 2)\n\n<\/pre>\n<p>Even if we run our doctest after this changes, we got no error, but we are not testing the negative number yet. so, let&#8217;s add a fibonacci(-1) in our fibonacci.txt test.<\/p>\n<p>Just a heads up, if we run fibonacci(-1) we will get a exception like this:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\nTraceback (most recent call last):\n  File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;\n  File &quot;fibonacci.py&quot;, line 5, in fibonacci\n    raise ValueError('Number is negative')\nValueError: Number is negative\n<\/pre>\n<p>And you&#8217;re probably thinking that you can copy and paste this error on our fibonacci.txt and it will work, but\u00a0 what if the file can change? You can add more code on the file, and the line where the error is being raised (in this case, line 5) might change. Being so, doctest only cares about the first and last lines, so our code in fibonacci.txt file will look like this:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&gt;&gt;&gt; fibonacci(-1)\nTraceback (most recent call last):\nValueError: Number is negative\n<\/pre>\n<p>And we are done! now we have a set of tests to our fibonacci code!<\/p>\n<p>But what if you don&#8217;t want to have a txt and still have your tests? You can write doctest directly in your code on the docstring:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\ndef fibonacci(number):\n    '''\n    This is a fibonacci function.\n    Example of usage:\n    &gt;&gt;&gt; fibonacci(0)\n    0\n    &gt;&gt;&gt; fibonacci(1)\n    1\n    &gt;&gt;&gt; fibonacci(2)\n    1\n    &gt;&gt;&gt; fibonacci(3)\n    2\n    &gt;&gt;&gt; fibonacci(4)\n    3\n    &gt;&gt;&gt; fibonacci(7)\n    13\n    &gt;&gt;&gt; fibonacci(-1)\n    Traceback (most recent call last):\n    ValueError: Number is negative\n    '''\n    if number == 1 or number == 0:\n        return number\n    if number &lt; 0:\n        raise ValueError('Number is negative')\n    return fibonacci(number - 1) + fibonacci(number - 2)\n\n<\/pre>\n<p>and you can run python -m doctest fibonacci.py.<\/p>\n<h5>More<\/h5>\n<p>Doctest have more features, as you can see below:<\/p>\n<h6>ELLIPISIS<\/h6>\n<p>According our fibonacci code, if you pass a negative number, it will raise a ValueError exception with the message &#8216;Number is negative&#8217;. There are some cases, that we don&#8217;t need to know which message is returning, we just want to know that an exception is being raised. Another example is when the output is the default __repr__ output, every time is a different memory address and you can&#8217;t control it. For this kind of situations, we can use the ELLIPISIS like in the example below.<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\n&gt;&gt;&gt; fibonacci(-1) #doctest: +ELLIPSIS\nTraceback (most recent call last):\nValueError: ...\n\n<\/pre>\n<p>The line containing #doctest: + ELLIPSIS tells to doctest that the &#8216;&#8230;&#8217; can match anything after &#8216;ValueError: &#8216;<\/p>\n<h6>SKIP<\/h6>\n<p>As the name says, it skips a test. This can be usefull when you know that some test is failing due some know bug, but it&#8217;s not fixed yet.<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\n&gt;&gt;&gt; fibonacci(-1) #doctest: +ELLIPSIS +SKIP\nTraceback (most recent call last):\nValueError: ...\n<\/pre>\n<p>As you can see, you can use the two options, ELLIPSIS and SKIP together.<\/p>\n<h5>Conclusion<\/h5>\n<p>Tests can be fun and easy, and it helps to keep you code working properly in the whole cycle of development.<br \/>\nFor more information about doctest, visit <a href=\"https:\/\/docs.python.org\/2\/library\/doctest.html\">doctest website<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>So, I just started an interest on python tests and I would like to share what I learned so far. Let&#8217;s skip all that theory about how tests are important for the code, bla bla bla, if you want to learn test, probably you already have your own reason, so, let&#8217;s jump to the part &hellip; <a href=\"https:\/\/arxcruz.net\/index.php\/2015\/06\/13\/writing-tests-in-python-for-beginners-part-1-doctest\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Writing tests in python for beginners &#8211; Part 1 &#8211; doctest&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","jetpack_publicize_message":"Writing tests in python for beginners - Part 1 - doctest http:\/\/wp.me\/p4BheT-23","jetpack_is_tweetstorm":false},"categories":[3,6,1],"tags":[4,5,7,8],"jetpack_featured_media_url":"","jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8bIYB-23","jetpack-related-posts":[{"id":148,"url":"https:\/\/arxcruz.net\/index.php\/2017\/01\/08\/testing-http-requests-with-mock-or-httmock\/","url_meta":{"origin":127,"position":0},"title":"Testing http requests with mock or HTTMock?","date":"January 8, 2017","format":false,"excerpt":"So, these days I was working in a project that uses requests to make some http\/https calls: And as usual, I wanted to have my unit tests, and requests is very tricky, because, you don't want to keep making requests to the real server, every time you run your unit\u2026","rel":"","context":"In &quot;English&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":153,"url":"https:\/\/arxcruz.net\/index.php\/2017\/09\/21\/debugging-tempest\/","url_meta":{"origin":127,"position":1},"title":"Debugging tempest","date":"September 21, 2017","format":false,"excerpt":"Introduction One of the common tasks when you're working on TripleO is to debug tempest when you found some failures. This is very important because once you found an error, you need to provide all the information needed when you're gonna fill a bug, or you can even actually, find\u2026","rel":"","context":"In &quot;OpenStack&quot;","img":{"alt_text":"","src":"https:\/\/i2.wp.com\/arxcruz.net\/wp-content\/uploads\/2017\/09\/Selection_005-300x207.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":119,"url":"https:\/\/arxcruz.net\/index.php\/2015\/02\/05\/new-small-project-stackquery-dashboard\/","url_meta":{"origin":127,"position":2},"title":"New small project: stackquery-dashboard","date":"February 5, 2015","format":false,"excerpt":"I\u2019ve started a new project called stackquery-dashboard. Basically, it\u2019s a web interface to show reports about user contribution in OpenStack, using the\u00a0stackalytics.com\u00a0API and part of the code from launchpadstats\u00a0from\u00a0\u00a0Martina Kollarova. The first reason I start this project is because stackalytics.com only shows reports for each user, and sometimes managers in\u2026","rel":"","context":"In &quot;OpenStack&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/arxcruz.net\/index.php\/wp-json\/wp\/v2\/posts\/127"}],"collection":[{"href":"https:\/\/arxcruz.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/arxcruz.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/arxcruz.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/arxcruz.net\/index.php\/wp-json\/wp\/v2\/comments?post=127"}],"version-history":[{"count":0,"href":"https:\/\/arxcruz.net\/index.php\/wp-json\/wp\/v2\/posts\/127\/revisions"}],"wp:attachment":[{"href":"https:\/\/arxcruz.net\/index.php\/wp-json\/wp\/v2\/media?parent=127"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/arxcruz.net\/index.php\/wp-json\/wp\/v2\/categories?post=127"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/arxcruz.net\/index.php\/wp-json\/wp\/v2\/tags?post=127"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}