Test Suite¶
The TestSuite is a testing framework for both synchronous and
asynchronous applications and for running tests in parallel
on multiple threads or processes.
It is used for testing pulsar but it can be used
as a test suite for any other library.
Integration¶
Pulsar test suite can be used in conjunction with setuptools or stand-alone.
Setuptools Integration¶
Pulsar asynchronous test suite can be used as testing framework in your setuptools based project. Add this to setup.py file:
from setuptools import setup
setup(
#...,
setup_requires=['pulsar', ...],
#...,
)
And create an alias into setup.cfg file:
[aliases]
test=pulsar_test
If you now type:
python setup.py test
this will execute your tests using pulsar test runner. As this is a standalone version of pulsar no prior installation whatsoever is required for calling the test command. You can also pass additional arguments to pulsar test, such as your test directory or other options using -a.
Manual Integration¶
If for some reason you don’t want/can’t use the setuptools integration,
you can write a standalone script, for example runtests.py:
from pulsar.apps import TestSuite
if __name__ == '__main__':
TestSuite(description='Test suite for my library').start()
To run the test suite:
python runtests.py
Type:
python runtests.py --help
For a list different options/parameters which can be used when running tests.
Running Tests¶
Writing a Test Case¶
Only subclasses of TestCase are collected by this
application. An example test case:
import unittest
class MyTest(unittest.TestCase):
async def test_async_test(self):
result = await async_function()
self.assertEqual(result, ...)
def test_simple_test(self):
self.assertEqual(1, 1)
Note
Test functions are asynchronous, when they are coroutine functions or
return a Future, synchronous, when they
return anything else.
Loading Tests¶
The loading of test cases is controlled by the modules parameter when
initialising the TestSuite:
from pulsar.apps import TestSuite
if __name__ == '__main__':
TestSuite(modules=('tests', 'examples')).start()
When using pulsar test suite - setuptools integration, test modules are specified
in the setup.cfg:
[test]
test_modules = tests examples
Test files¶
The TestSuite loads tests via the loader
property. By default it recursively loads test files inside the
modules directories matching:
test_<label>.py<label>_test.pytests.py
Test labels¶
To run a specific label:
python runtests.py <label>
When using the setuptools integration:
python setup.py test -a <label>
To list all labels:
python setup.py test -l
It is also possible to run a single test function within a label:
python setup.py test -a <label>.<test_function_name>
Options¶
All standard settings can be applied to the test application. In addition, the following options are test suite specific:
sequential¶
By default, test functions within a TestCase
are run in asynchronous fashion. This means that several test functions
may be executed at once depending on their return values.
By specifying the –sequential command line option,
the TestSuite forces test functions from a given
TestCase to be run in a sequential way,
one after the other:
python runtests.py --sequential
Alternatively, if you need to specify a TestCase which
always runs its test functions in a sequential way, you can use
the sequential() decorator:
from pulsar.apps.test import sequential
@sequential
class MyTestCase(unittest.TestCase):
...
list labels¶
By passing the -l or –list-labels flag
to the command line, the full list of test labels available is displayed:
python runtests.py -l
test timeout¶
When running asynchronous tests, it can be useful to set a cap on how long a test function can wait for results. This is what the –test-timeout command line flag does:
python runtests.py --test-timeout 10
Set the test timeout to 10 seconds. Test timeout is only meaningful for asynchronous test function.
Http TestClient¶
The HttpTestClient can be used to test wsgi middleware without going
through socket connections.
To use the client in a test function:
async def test_my_wsgi_test(self):
http = HttpTestClient(self, wsgi)
response = await http.get('http://bla.com/...')
The host part of the url is irrelevant, it can be anything you like.
Test Plugins¶
A TestPlugin is a way to extend the test suite with additional
options and behaviours implemented in
the various plugin’s callbacks.
There are two basic rules for plugins:
- Test plugin classes should subclass
TestPlugin. - Test plugins may implement any of the methods described in the class
Plugininterface.
Pulsar ships with two battery-included plugins:
Benchmark¶
BenchMark is a TestPlugin for benchmarking test functions.
To use the plugin follow these three steps:
Flag a
unittest.TestCaseclass with the__benchmark__ = Trueclass attribute:class MyBenchmark(unittest.TestCase): __benchmark__ = True def test_mybenchmark_function1(self): ... def test_mybenchmark_function2(self): ...
Run the test suite with the
--benchmarkcommand line option.
Alternatively, you can use the bench command from your setup.py file.
Simply add the bench entry in the setup.cfg.
[bench] test_modules = tests/bench
The test class can implement additional methods to fine-tune how the benchmark plugin evaluate the performance and display results:
- When implemented, the
startUpmethod is invoked before each run of a test function. - The time taken to run a test once can be modified by implementing
the
getTimemethod which receives as only argument the time interval taken. By default it returns the same time interval.
Profile¶
Profile is a TestPlugin for profiling
test cases and generating Html reports.
It uses the cProfile module from the standard library.
To use the plugin follow these two steps:
Included it in the test Suite:
from pulsar.apps.test import TestSuite from pulsar.apps.test.plugins import profile def suite(): TestSuite(..., plugins=(..., profile.Profile()))
Run the test suite with the
--profilecommand line option.
API¶
Test Suite¶
-
class
pulsar.apps.test.TestSuite(callable=None, load_config=True, **params)[source]¶ An asynchronous test suite which works like a task queue.
Each task is a group of test methods in a python TestCase class.
Parameters: - modules – An iterable over modules where to look for tests.
If not provided it is set as default to
["tests"]which loads all python module from the tests module in a recursive fashion. Check the theTestLoaderfor detailed information. - plugins – Optional list of dotted path to
TestPluginclasses.
-
loader¶ Instance of the
TestLoaderused for loading test cases
- modules – An iterable over modules where to look for tests.
If not provided it is set as default to
Test Loader¶
-
class
pulsar.apps.test.loader.TestLoader(suite)[source]¶ Classes used by the
TestSuiteto aggregate tests from a list of paths.The way it works is simple, you give a root directory and a list of submodules where to look for tests.
Parameters: - root – root path passed by the
TestSuite. - modules – list (or tuple) of entries where to look for tests. Check loading test documentation for more information.
- runner – The
TestRunnerpassed by the test suite.
Return a generator of tag information
- root – root path passed by the
Plugin¶
-
class
pulsar.apps.test.result.Plugin[source]¶ Interface for all classes which are part of the
TestRunner.Most classes used by the test application are plugins, for example the
TestRunneritself, theTestResultand theTestPlugin.-
result= None¶ An optional result
-
stream= None¶ handle for writing text on the default output.
Set by the
TestRunnerat runtime.
-
configure(cfg)[source]¶ - Called once just after construction of a
TestRunner - and before any test class is loaded.
This is a chance to configure the
Pluginor global variables which may affect the way tests are run. If it returns something other thanNone(for example an abort message) it will stop the configuration of all subsequent plugins and quit the test.Parameters: cfg – a Config.Returns: Noneunless the tests runner must be stopped.- Called once just after construction of a
-
on_start()[source]¶ Called by the
TestSuiteonce only at startup.This callback is invoked once all tests are loaded but before the test suite starts running them.
-
loadTestsFromTestCase(testcls)[source]¶ Called when loading tests from the
testclsclass.Can be used to modify the number of test functions loaded.
-
startTest(test)[source]¶ Called just before a
testfunction is executed.This is run just before
_pre_setupmethod.
-
stopTest(test)[source]¶ Called just after a
testfunction has finished.This is run just after the
_post_teardownmethod.
-
before_test_function_run(test, local)[source]¶ Can be used by plugins to manipulate the
testbehaviour in the process domain where the test run.
-
Test Runner¶
Test Result¶
-
class
pulsar.apps.test.result.TestResult(descriptions=True)[source]¶ A
Pluginfor collecting results/failures for test runs.Each
Plugincan access theTestRunnerresultobject via theresultattribute.-
addError(test, err)[source]¶ Called when an unexpected error has occurred.
erris a tuple of values as returned bysys.exc_info()
-
Test Plugin¶
-
class
pulsar.apps.test.plugins.base.TestPlugin[source]¶ Base class for
Pluginwhich can be added to aTestSuiteto extend its functionalities.If the class attribute
nameis not specified or its value validate asTrue, an additional setting is added to the configuration. In addition, aTestPlugincan specify several additional settings as class attributes. For example, the benchmark plugin has an additional setting for controlling the number of repetitions:class Bench(TestPlugin): repeat = pulsar.Setting(type=int, default=1, validator=pulsar.validate_pos_int, desc="Default number of repetition")
-
name¶ Class attribute used for adding the default plugin setting to the configuration container of the test suite application. If the attribute is not set, the class name in lower case is used. If set and validate as not
True, no new setting is added to the test suite configuration parameters. For example:class MyPlugin(TestPlugin): name = None
won’t add the default plugin setting.
-
Populate¶
A useful function for populating random data:
from pulsar.apps.test import populate
data = populate('string', 100)
gives you a list of 100 random strings
-
pulsar.apps.test.populate.populate(datatype='string', size=10, start=None, end=None, converter=None, choice_from=None, **kwargs)[source]¶ Utility function for populating lists with random data.
Useful when populating database with data for fuzzy testing.
Supported data-types
- string
For example:
populate('string', 100, min_len=3, max_len=10)
create a 100 elements list with random strings with random length between 3 and 10
- date
For example:
from datetime import date populate('date', 200, start=date(1997,1,1), end=date.today())
create a 200 elements list with random datetime.date objects between start and end
- integer
For example:
populate('integer', 200, start=0, end=1000)
create a 200 elements list with random integers between
startandend
- float
For example:
populate('float', 200, start=0, end=10)
create a 200 elements list with random floats between
startandend
- choice (elements of an iterable)
For example:
populate('choice', 200, choice_from=['pippo','pluto','blob'])
create a 200 elements list with random elements from
choice_from
Utilities¶
sequential¶
-
pulsar.apps.test.utils.sequential(cls)[source]¶ Decorator for a
TestCasewhich cause its test functions to run sequentially rather than in an asynchronous fashion.Typical usage:
import unittest from pulsar.apps.test import sequential @sequenatial class MyTests(unittest.TestCase): ...
You can also run test functions sequentially when using the sequential flag in the command line.
ActorTestMixin¶
-
class
pulsar.apps.test.utils.ActorTestMixin[source]¶ A mixin for
TestCase.Useful for classes testing spawning of actors. Make sure this is the first class you derive from, before the
TestCase, so that the tearDown method is overwritten.-
concurrency¶ The concurrency model used to spawn actors via the
spawn()method.
-