Upgrade to Pro — share decks privately, control downloads, hide ads and more …

python TDD

python TDD

introduction into testing with Python

Yura Hulpa

March 10, 2016
Tweet

More Decks by Yura Hulpa

Other Decks in Programming

Transcript

  1. TDD Test-driven development (TDD) is a so ware development process

    that relies on the repetition of a very short development cycle: first the developer writes an (initially failing) automated test case that defines a desired improvement or new function, then produces the minimum amount of code to pass that test, and finally refactors the new code to acceptable standards
  2. TDD - Kent Beck • One of the populalarizator of

    TDD approach • Inventor of Extreme programming • Author of SUnit unit testing approach for SmallTalk language • Co-author of “Refactoring: Improving the Design of Existing Code”
  3. uni est — Unit testing framework The Python unit testing

    framework, sometimes referred to as “PyUnit,” is a Python language version of JUnit, by Kent Beck and Erich Gamma. JUnit is, in turn, a Java version of Kent’s Smalltalk testing framework. Each is the de facto standard unit testing framework for its respective language.
  4. Main idioms • Always run the full test suite before

    a coding session, and run it again a er. • It is a good idea to implement a hook that runs all tests before pushing code to a shared repository. • A testing unit should focus on one tiny bit of functionality and prove it correct. • Each test unit must be fully independent. Each of them must be able to run alone, and also within the test suite, regardless of the order they are called. • each test must be loaded with a fresh dataset and may have to do some cleanup a erwards. This is usually handled by setUp() and tearDown() methods.
  5. Building blocks of Python tests Test case A test case

    is the smallest unit of testing. It checks for a specific response to a particular set of inputs. uni est provides a base class, TestCase, which may be used to create new test cases.
  6. Let’s create a test To manipulate data in human readable

    form from unittest import TestCase class MyTestCase(TestCase): pass
  7. TDD approach - test first, code - a er To

    manipulate data in human readable form # functions_to_test.py def multiply(x, y): pass # test_me.py from functions_to_test import multiply from unittest import TestCase class DecoratorTestCase(TestCase): def test_multiple_simple(self): result = multiply(2,2) self.assertEqual(result, 4)
  8. Run 0 - Result Failure Traceback (most recent call last):

    File "test_me.py", line 10, in test_no_wrap self.assertEqual(result, 4) AssertionError: None != 4
  9. Write code. Run 1 - Result - Passed Add some

    functionality to existing function, method def multiply(x, y): return x*y
  10. Mocking everything Python Mock lib It allows you to replace

    parts of your system under test with mock objects and make assertions about how they have been used. It is a part of Python Standart lib (since 3 version), for Python 2.7 you should install it with Pip
  11. Memoized decorator Add some functionality to existing function, method import

    collections import functools class memoized(object): '''Decorator. Caches a function's return value each time it is called. If called later with the same arguments, the cached value is returned (not reevaluated). ''' def __init__(self, func): self.func = func self.cache = {} def __call__(self, *args): if not isinstance(args, collections.Hashable) : # uncacheable. a list, for instance. # better to not cache than blow up. return self.func(*args) if args in self.cache: return self.cache[args] else: value = self.func(*args) self.cache[args] = value return value def __repr__(self): '''Return the function's docstring.''' return self.func.__doc__ def __get__(self, obj, objtype): '''Support instance methods.''' return functools.partial(self.__call__, obj)
  12. Writing a test for decorators with Mock Add some functionality

    to existing function, method from mock import Mock from unittest import TestCase from decorators import memoized class DecoratorTestCase(TestCase): def test_wrap_memoized(self): my_fn = Mock(name='my_fn') my_fn.return_value = 4 wrapped = memoized(my_fn) # First call gives a call count of 1 self.assertEqual(wrapped(3), 4) self.assertEqual(my_fn.call_count, 1) self.assertEqual(wrapped(3), 4) self.assertEqual(my_fn.call_count, 1) self.assertEqual(wrapped(7), 4) self.assertEqual(my_fn.call_count, 2)
  13. Patching everything Sometimes we need to make some function behave

    like we need. Here patch decorators come to help us
  14. Our BitCoin Code Add some functionality to existing function, method

    import requests import datetime API_URL = "https://www.bitstamp. net/api/transactions/?time=hour" # url that provide API for fetching transactions. class BitCoinTransaction: def __init__(self, date, tid, price, amount): self.date = date self.tid = tid self.price = price self.amount = amount @classmethod def dictionary_to_instance(cls, start_dict): """ Return a BitCoinTransaction class instance """ date = datetime.datetime.fromtimestamp(int( start_dict["date"])) tid = int(start_dict["tid"]) price = float(start_dict["price"]) amount = float(start_dict["amount"]) return cls(date, tid, price, amount) def get_bitcoin_transactions_data(api_url): """ Return a decoded JSON """ raw_bitcoin_data = requests.get(api_url) json_bitcoin_data = raw_bitcoin_data.json() return json_bitcoin_data
  15. Our BitCoin Test 1 Add some functionality to existing function,

    method from mock import Mock, patch from unittest import TestCase from functions_to_test import from_bitcoin_transaction_list, get_bitcoin_transactions_data, \ BitCoinTransaction, API_URL class BitcointTestCase(TestCase): def setUp(self): self.response = [ {u'date': u'1457617803', u'tid': 10769905, u'price': u'412.53', u'type': 1, u'amount': u'0.05937100'} ] @patch('requests.get') def test_get_bitcoin_transactions_data(self, mock_get ): mock_response = Mock() mock_response.json.return_value = self. response mock_get.return_value = mock_response result = get_bitcoin_transactions_data( API_URL) self.assertEqual(len(result), 1)
  16. Our BitCoin Test 2 Add some functionality to existing function,

    method from mock import Mock, patch from unittest import TestCase from functions_to_test import from_bitcoin_transaction_list, get_bitcoin_transactions_data, \ BitCoinTransaction, API_URL class BitcointTestCase(TestCase): def setUp(self): self.response = [ {u'date': u'1457617803', u'tid': 10769905, u'price': u'412.53', u'type': 1, u'amount': u'0.05937100'} ] @patch('requests.get') def test_from_bitcoin_transaction_list(self, mock_get ): mock_response = Mock() mock_response.json.return_value = self. response mock_get.return_value = mock_response transaction_data = get_bitcoin_transactions _data(API_URL) result = from_bitcoin_transaction_list( transaction_data) self.assertEqual(len(result), 1) self.assertIsInstance(result[0], BitCoinTransaction)