Introduction
- Benefits of Unit testing
Python tools
- Python different test tools.
Writing tests for different scenarios
- Simple test case
- data driven/Decorated test (@ddt)
- Generative testing
- Mock
- The Mock object
- Different ways of using
- Patch
Running tests
- Command line interface
Integrating with CI (Travis)
- Github and Travis example
the standard library included in Python.
Familiar to anyone who has used any of JUnit/nUnit/CppUnit series of tools
Also popular and an alternative to Python's standard unittest module.
It boasts a simple syntax
Simple test case (to get started)
import json
import datetime
class activity_ConvertJATS():
def add_update_date_to_json(self, json_string, update_date):
try:
json_obj = json.loads(json_string)
updated_date = datetime.datetime.strptime(update_date, "%Y-%m-%dT%H:%M:%SZ")
update_date_string = updated_date.strftime('%Y-%m-%dT%H:%M:%SZ')
json_obj['update'] = update_date_string
json_string = json.dumps(json_obj)
except:
if self.logger:
self.logger.error("Unable to set the update date in the json")
return json_string
import unittest
from simple_sample import activity_ConvertJATS
import json
input = open("tests/test_data/input.json","r").read()
output = json.loads(open("tests/test_data/output.json","r").read())
class MyTestCase(unittest.TestCase):
def test_add_update_to_json(self):
self.jats = activity_ConvertJATS()
result = self.jats.add_update_date_to_json(input,'2012-12-13T00:00:00Z')
self.assertDictEqual(json.loads(result), output)
if __name__ == '__main__':
unittest.main()
class activity_DepositAssets():
def get_no_download_extensions(self, no_download_extensions):
return [x.strip() for x in no_download_extensions.split(',')]
import unittest
from ddt import ddt, data, unpack
from sample import activity_DepositAssets
@ddt
class MyTestCase(unittest.TestCase):
def setUp(self):
self.depositassets = activity_DepositAssets()
@unpack
@data({'input': '.tif', 'expected': ['.tif']},
{'input': '.jpg, .tiff, .png', 'expected':['.jpg', '.tiff', '.png']})
def test_get_no_download_extensions(self, input, expected):
result = self.depositassets.get_no_download_extensions(input)
self.assertListEqual(result, expected)
if __name__ == '__main__':
unittest.main()
sample.py
tests/test_sample_ddt_unpack.py
Improve product quality and find bugs faster by generating tests
Property based testing is a method of testing functions pioneered by the Haskell community. From Hackage:
QuickCheck is a library for random testing of program properties.
The programmer provides a specification of the program, in the form of properties which functions should satisfy, and QuickCheck then tests that the properties hold in a large number of randomly generated cases.
def encode(input_string):
count = 1
prev = ''
lst = []
for character in input_string:
if character != prev:
if prev:
entry = (prev, count)
lst.append(entry)
count = 1
prev = character
else:
count += 1
else:
entry = (character, count)
lst.append(entry)
return lst
def decode(lst):
q = ''
for character, count in lst:
q += character * count
return q
from hypothesis import given
from hypothesis.strategies import text
@given(text())
def test_decode_inverts_encode(s):
assert decode(encode(s)) == s
First try
Falsifying example: test_decode_inverts_encode(s='')
UnboundLocalError: local variable 'character' referenced before assignment
This code is simply wrong when called on an empty string.
Fix
if not input_string:
return []
"Sometimes, you need "other" code resources for your test setup. But those resources may be unavailable, unstable, or just too unwieldy to use. You could try and find a replacement for the missing resource; or you could simulate it by creating what is known as a mock. Mocks let us simulate resources that are either unavailable or too unwieldy for unit testing."
http://www.drdobbs.com/testing/using-mocks-in-python/240168251
Mocking in Python is done by using patch to hijack an API function or object creation call. When patch intercepts a call, it returns a MagicMock object by default. By setting properties on the MagicMock object, you can mock the API call to return any value you want or raise an Exception.
https://blog.fugue.co/2016-02-11-python-mocking-101.html
@patch('requests.post')
def test_schedule_article_publication(self, mock_requests_post):
mock_requests_post.return_value = FakeResponse(200, {'result': 'success'})
input = '{"articles":{"article-identifier":"03430","scheduled":"1463151540"}}'
resp = self.client.post('/api/schedule_article_publication', data=input)
self.assertDictEqual(json.loads(resp.data), {'result': 'success'})
Most used attributes of a MagicMock instance:
return_value
@data(data_published_lax)
@patch.object(activity_VerifyPublishResponse, 'publication_authority')
@patch.object(activity_VerifyPublishResponse, 'emit_monitor_event')
def test_do_activity(self, data, fake_emit_monitor, fake_publication_authority):
fake_publication_authority.return_value = "elife2.0"
result = self.verifypublishresponse.do_activity(data)
fake_emit_monitor.assert_called_with(settings_mock,
data["article_id"],
data["version"],
data["run"],
self.verifypublishresponse.pretty_name,
"end",
"Finished Verification " + data["article_id"])
self.assertEqual(result, self.verifypublishresponse.ACTIVITY_SUCCESS)
assert_called_with
Running unit tests
Examples:
python -m unittest test_module - run tests from test_module
python -m unittest module.TestClass - run tests from module.TestClass
python -m unittest module.Class.test_method - run specified test method
The unittest module can be used from the command line to run tests from modules, classes or even individual test methods: