Package: agile
Meta-package for python with tools for an agile development workflow.add it to your projectpipinstallagilewhat is in it?mockThemocklibrary is the
easiest and most expressive way to mock in python.example: mocking I/O calls:# cool-git-project/my_cool_library/core.pyimportioimportjsonclassJSONDatabase(object):def__init__(self,filename=None,data={}):self.filename=filenameself.data=datadefstate_to_json(self):returnjson.dumps(self.data)defsave(self):# open filefd=io.open(self.filename,'wb')fd.write(self.state_to_json())fd.close()# cool-git-project/tests/unit/test_core.pyfrommockimportpatchfrommy_cool_library.coreimportJSONDatabase@patch('my_cool_library.core.io')@patch('my_cool_library.core.JSONDatabase.state_to_json')deftest_json_database_save(state_to_json,io):("JSONDatabase.save() should open the database file, ""and write the latest json state of the data")# Given that the call to io.open returns a mockmocked_fd=io.open.return_value# And that I create an instance of JSONDatabase with some datajdb=JSONDatabase('my-database.json',data={'foo':'bar'})# When I call .save()jdb.save()# Then the file descriptor should have been opened in write mode,# and pointing to the right fileio.open.assert_called_once_with('my-database.json','wb')# And the returned file descriptor should have been used# to write the return value from state_to_jsonmocked_fd.write.assert_called_once_with(state_to_json.return_value)# And then the file descriptor should have been closedmocked_fd.close.assert_called_once_with()The mock documentation can be foundheresureSure modifies the all the python objects in memory, adding a special
propertyshould, that allows you to test aspects of the given
object.Let’s see it in practice.Still considering the project from themockexample above, now let’s
test thatstate_to_jsonreturns a json string.deftest_json_database_state_to_json():("JSONDatabase.state_to_json() should return a valid json string")# Given that I have an instance of the database containing some datajdb=JSONDatabase(data={'name':'Foo Bar'})# When I call .state_to_jsonresult=jdb.state_to_json()# Then it should return a valid JSONresult.should.equal('{"name": "Foo Bar"}')The sure documentation is availableherenose + coverage + rednosenosetests-vsx--rednose--with-coverage--cover-package=my_cool_librarytests/unit# ornosetests-vsx--rednose--with-coverage--cover-package=my_cool_librarytests/functionalNose is a great test runner, recursively scans for files that start withtest_and and with.py. It supports plugins and agile installs
two cool plugins:coveragecoverage is a module that collects test coverage data so that nose can
show a summary of what lines of python code don’t have test coverage.rednoseRednose is a plugin that prints a prettier output when running the
tests, and show bad things inredwhich highlights problems and make
it easier to see where is the problem, pretty awesome.More over,as long as you write single-line docstrings to describe
your testsrednose will show the whole sentence, pretty and with no
chops.JSONDatabase.save()shouldopenthedatabasefile,andwritethelatestjsonstateofthedata...passedJSONDatabase.state_to_json()shouldreturnavalidjsonstring...passed-----------------------------------------------------------------------------2testsrunin0.0seconds(2testspassed)ps.: nose actually matches files that containtestin the name and
can also findTestCaseclasses, but I recommend using function-based
tests, for clarity, expressiveness and to enforce simplicity. We
developers tend to add too much logic to setup and teardown functions
when writing test-based class.Gists:creating a basic python test infrastructuremkdir-ptests/{unit,functional}touchtests/{unit,functional,}/__init__.pyprintf'import sure\nsure\n'>tests/unit/__init__.pyprintf'import sure\nsure\n'>tests/functional/__init__.pynow go ahead and add a unit test file, try to name your test file such
that it resembles module being tested, for example, let’s say you are
testingmy_cool_library/engine.py, you could create a test file like
thisprintf"# -*- coding: utf-8 -*-\n\n">tests/unit/test_engine.py