assert causes an error when a condition fails, so it's a good way to make sure that if something breaks in your code you know about it right away, before the error propagates to somewhere else. Try out the example below:
def make_neuron_array(array_size):
neuron_array = [0] * array_size
assert len(neuron_array) == array_size
return neuron_array
For info, check out this article.
ipdb.set_trace() can be used to open an iPython shell wherever in your code you have a problem. You can also step through the code using n. For example, after you've installed ipdb using pip try running this code and stepping through it:
import ipdb
print("start")
vals = [3, 1, 4]
ipdb.set_trace()
vals[0] += 5
print("done")
You can also use ipdb to open a debug shell whenever an exception occurs using the following code, taken from a StackOverflow question:
import sys
from IPython.core import ultratb
sys.excepthook = ultratb.FormattedTB(mode='Verbose',
color_scheme='Linux', call_pdb=1)
Forcing your code into little functions (that do one thing) so you can test in an automated manner.
Why?
Python includes a unit testing module by default, but it has a lot boilerplate. Instead use PyTest!
The following examples use the file structure defined in Installation - setuptools.ipynb, repeated below:
|- README.md
|- killer
| |-- __init__.py
| |-- kill.py
| | -- tests
| | |-- test_kill.py
In test_kill.py let's make the most basic test possible.
import pytest
import killer.kill as kk
def test_spray():
kk.spray()
Note that all test functions have to start with the word test. Now run it with py.test test_kill.py and watch the test pass.
Basic Fixtures
In kill.py two more functions that take the same argument, but do different stuff with it.
def throw(thing):
print("Throwing %s!" %(thing,))
def kick(thing):
print("Kicking %s!" %(thing,))
To test these functions, we don't want to define the argument twice, so instead we use a fixture. Add the following code to test_kill.py.
@pytest.fixture
def thing():
return "socks"
def test_kick(thing):
kk.kick(thing)
def test_kick(thing):
kk.throw(thing)
PyTest suppresses any print statements, so to convince yourself the argument is really being passed, comment out the fixture and notice that the tests fail.
Nengo Fixtures
Nengo has some useful fixtures built in for plotting the results of tests, setting random seeds, as well as logging the outputs and speed of test execution. To use them, add nengo.conftest import *. To see them in action, just run the tests in Nengo and look at the outputs.
You can also group tests in a class like so:
class TestBasic(object):
def test_spray(self):
kk.spray()
def test_shoot(self):
kk.shoot()
This also means that you can inherit classes for testing. For a good example of how and why to do this, check out this pull request by Jan where he tests copying Nengo objects with minimal repeating code.
conftest.py So You "Don't Repeat Yourself"If you need fixtures or classes that should be shared accross various testfiles, you can put them in conftest.py. For example, if you needed the thing fixture for other testfiles, you could move it to conftest.py and it would be found automatically when you ran tests.