Developer Guide¶
This (very incomplete) document aims at providing some guidance for current and
future developers working on aioxmpp
.
Testing¶
aioxmpp
is developed in test-driven development style. You can read up on
the internet what this means in detail, but it boils down to “write code only to
fix tests and write tests to justify writing code”.
This implies that the aioxmpp
test suite is pretty extensive, and using
the default python unittest runner is not very useful. The recommended test
runner to use is Nose. Nose can be
invoked directly (from within the aioxmpp source repository) on the test suite:
$ nosetests3 tests
End-to-end tests (or integration tests)¶
Note
The module aioxmpp.e2etest
also contains quite a bit of information on
the framework and configuration. This probably needs a bit of clean up to
consolidate and deduplicate information.
The normal unittest suite is quite nice, but it consists mostly of unit tests,
which have an important flaw: the interaction between units is not well
tested. There are a few exceptions, such as tests/test_highlevel.py
which
tests very few operations through the whole stack (very few, because it is very
cumbersome to write tests in that manner).
To remedy that, aioxmpp
features a specialised test runner which allows
for running aioxmpp
tests against a real XMPP server. It requires a bit
of configuration (read on), so it won’t work out of the box. It can be invoked
using:
$ python3 -m aioxmpp.e2etest tests
It needs to be configured though. For this, an ini-style configuration file
(using configparser
) is read. The default location is
./.local/e2etest.ini
, but it can be overridden with the --e2etest-config
command line option.
Note
aioxmpp.e2etest
uses Nose for everything and patches in a plugin and a
few helper functions to provide the advanced testing functionality. This is
also why the vanilla nosetests runner doesn’t break on the test cases.
The following global configuration options exist:
[global]
timeout=1
provisioner=
provisioner
must be set to point to a Python class which inherits from
aioxmpp.e2etest.provision.Provisioner
. The above value is an example.
Each provisioner has different configuration options. The different provisioners
are explained in detail below.
timeout
specifies the default timeout for each individual test in seconds.
The default is 1 second. If you have a slow connection to the server, it may be
reasonable to increase this to a higher value.
To test that you got your configuration correct, use:
$ python3 -m aioxmpp.e2etest tests/test_e2e.py:TestConnect
This should run a single test, which should pass.
Anonymous provisioner¶
The anonymous provisioner uses the ANONYMOUS
SASL mechanism to authenticate
with the target XMPP server. This is the most simple provisioner conceivable. An
example config file using that provisioner looks like this:
[global]
provisioner=aioxmpp.e2etest.provision.AnonymousProvisioner
[aioxmpp.e2etest.provision.AnonymousProvisioner]
domain=localhost
pin_store=pinstore.json
pin_type=0
The aioxmpp.e2etest.provision.AnonymousProvisioner
contains the options
specific to that provisioner.
domain
must be a valid JID domainpart and the XMPP host to connect to. This
must be a domain served by the target XMPP server. To connect to a local server
whose hostname and IP address cannot be resolved from the XMPP domain name via
the DNS, you can explicitly set the IP or hostname as well as the port to
connect to with the host
and port
options. If domain
is omitted but
host
is set, it is assumed to be the same as host
.
pin_store
and pin_type
can be used to configure certificate pinning, in
case the server you want to test against does not have a certificate which
passes the default OpenSSL PKIX tests.
If set, pin_store
must point to a JSON file, which consists of a single
object mapping host names to arrays of strings containing the base64
representation of what is being pinned. This is determined by pin_type
,
which can be 0
for Public Key pinning and 1
for Certificate pinning.
There is also the no_verify
option, which, if set to true, will disable
certificate verification altogether. This does not much harm if you are testing
against localhost anyways and saves the configuration nuisance for certificate
pinning. no_verify
takes precedence over pin_store
and pin_type
.
Writing end-to-end tests¶
For now, please see tests/test_e2e.py
as a reference. A few key points:
Make sure to inherit from
aioxmpp.e2etest.TestCase
instead ofunittest.TestCase
. This will prevent the tests from running with the normal nosetests runner and also give you the current provisioner asself.provisioner
.The
aioxmpp.e2etest.blocking()
decorator can be used everywhere to convert a coroutine function to a normal function. It works by wrapping the coroutine function in aasyncio.BaseEventLoop.run_until_complete()
call, with the usual implications.You do not need to clean up the clients obtained from the provisioner; the provisioner will stop them when the test is over (as if by using a
tearDown
method).Depending on the provisioner, the number of clients you can use at the same time may be limited; the anonymous provisioner has no limit.