There are various testing frameworks for python, but I personally like the framework called nose, so I would like to introduce it.
$ pip install nose
You may need sudo depending on your environment.
Suppose you have the following function sum and function is_even in a module called hoge.
hoge.py
def sum(a, b):
return a + b
def is_even(n):
return (n % 2 == 0)
Create the following file test_hoge.py that inherits unittest.TestCase.
Here, ʻeq_ to check if the value is the same as the expected ** value ** and ʻok_
to check if the value is ** true ** are imported from nose.tools
and used. I will.
As a common mechanism in the test framework, setUp
and tearDown
are provided for processing such as initialization / resource release before and after the test, so try inserting a print statement.
test_hoge.py
from unittest import TestCase
from nose.tools import ok_, eq_
from hoge import sum, is_even
class HogeTestCase(TestCase):
def setUp(self):
print 'before test'
def tearDown(self):
print 'after test'
def test_sum(self):
eq_(sum(1, 2), 3)
eq_(sum(5, 11), 16)
eq_(sum(0, 0), 0)
def test_is_even(self):
ok_(is_even(2))
ok_(not is_even(3))
nose comes with a test runner command called nosetests. (It will be installed by pip install nose) If you execute nosetests here, it will be as follows.
$ nosetests
..
----------------------------------------------------------------------
Ran 2 tests in 0.003s
OK
When nosetests
is executed, it will search for test-like files (including test in the file name) under the current directory, and will collect and execute classes that inherit from ʻunittest.TestCase. I didn't get the message that I should have printed with the print statement, because by default nose captures the output to standard output. You can see the output as is with the
-s` option.
$ nosetests -s
Add the -v
option to display the name of the test case you are executing by the method name as well as the dot.
$ nosetests -v
When I tried to execute it together, it became as follows.
$ nosetests -s -v
test_is_even (test_hoge.HogeTestCase) ... before test
after test
ok
test_sum (test_hoge.HogeTestCase) ... before test
after test
ok
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
You can also specify a file name for the nosetests command to run only tests on specific files. It would be convenient if development progressed and tests increased.
$ nosetests ./test_hoge.py
Here are some other test tools for ʻok_ and ʻeq_
. See nose's Testing tools page (http://nose.readthedocs.org/en/latest/testing_tools.html) for more information.
raises
Use raises
as a decorator for tests that are likely to cause exceptions.
Test the following function div
where an exception is likely to occur.
fuga.py
def div(n, m):
return n / m
The test case is below.
fuga_hoge.py
from unittest import TestCase
from nose.tools import eq_, raises
from fuga import div
class FugaTestCase(TestCase):
def test_div(self):
eq_(div(10, 5), 2)
eq_(div(10, 10), 1)
eq_(div(9, 3), 3)
@raises(ZeroDivisionError)
def test_div_zerodiv(self):
div(10, 0)
Now you can check if the test case test_div_zerodiv
is properly throwing the exception ZeroDivisionError
.
timed
timed
is a decorator that checks if a test case finishes within a specified amount of time (specified in seconds).
Consider a test that checks that a function do_something
, which takes about 0.1 seconds to process, finishes within 0.2 seconds, such as:
moge.py
import time
def do_something():
time.sleep(0.1)
The test case looks like this:
test_moge.py
from unittest import TestCase
from nose.tools import timed
from moge import do_something
class MogeTestCase(TestCase):
@timed(0.2)
def test_do_something(self):
do_something()
For programs that use gevent, it's a good idea to use the following decorators.
gtest decorator
import gevent
def gtest(func):
def _gtest(*args, **kwargs):
gevent.spawn(func, *args, **kwargs).join()
_gtest.__name__ = func.__name__
return _gtest
Let's test a program like the one below. _tick
and _tacik
move alternately, sending numbered strings such as tick1, tack1, tick2, tack2 to the queue alternately, and finally sucking out all the contents from the queue and returning it as an array. Will give you.
g.py
import gevent
import gevent.queue
def ticktack(n):
q = gevent.queue.Queue()
def _tick(_q, _n):
for i in range(_n):
_q.put('tick%d' % (i + 1))
gevent.sleep()
def _tack(_q, _n):
for i in range(_n):
_q.put('tack%d' % (i + 1))
gevent.sleep()
_tick_thread = gevent.spawn(_tick, q, n)
_tack_thread = gevent.spawn(_tack, q, n)
result = []
while len(result) < n * 2:
result.append(q.get())
return result
Since gevent makes a decisive greenlet switch when gevent.sleep () is called, the spawned _tick first and the spawned _tack later are always called.
Let's test this with the decorator above.
test_g.py
from unittest import TestCase
from nose.tools import eq_
import gevent
from g import ticktack
import gevent
def gtest(func):
def _gtest(*args, **kwargs):
gevent.spawn(func, *args, **kwargs).join()
_gtest.__name__ = func.__name__
return _gtest
class GTestCase(TestCase):
@gtest
def test_ticktack(self):
r1 = ticktack(1)
eq_(r1, ['tick1', 'tack1'])
r2 = ticktack(2)
eq_(r2, ['tick1', 'tack1', 'tick2', 'tack2'])
Did you feel good?
--nose makes it easy and easy to write test cases --nosetests has various options and can be executed flexibly --If you devise a test case for a program that uses gevent, it's ok.
I hope it helps.
Recommended Posts