Using ldap_faker with unittest
Most of the purpose of python-ldap-faker
is to make automated testing
of code that uses python-ldap
easier.
To this end, python-ldap-faker
provides LDAPFakerMixin
, a mixin class
for unittest.TestCase
which handles all the hard work of patching
and instrumenting the appropriate python-ldap
functions, objects and
methods.
LDAPFakerMixin
will do the following things for you:
Read data from JSON fixture files to populate one or more
ObjectStore
objects (our fake LDAP server class)Associate those
ObjectStore
objects with particular LDAP URIsPatch
ldap.initialize
to returnFakeLDAPObject
objects configured with the appropriateObjectStore
for the LDAP URI passed intoFakeLDAP.initialize
Configuring your LDAPFakerMixin TestCase
We need to set two class attributes on LDAPFakerMixin
in order for
it to properly set up your tests:
LDAPFakerMixin.ldap_modules
: The list of your code’s modules in which to patchldap.initialize
,ldap.set_option
andldap.get_option`
LDAPFakerMixin.ldap_fixtures
: A list of JSON fixture files with which to create theObjectStore
objects
LDAPFakerMixin.ldap_modules
LDAPFakerMixin
uses unittest.mock.patch
to patch your
code so that it uses our fake versions of ldap.initialize
,
ldap.set_option
and ldap.get_option
instead of the real
one. The way patch
works is that it must apply the patch within the context
of your module that does import ldap
, not within the ldap
module itself.
Thus, to make LDAPFakerMixin
work for you, you must list all the
modules for code under test in which you do import ldap
.
To list all the modules in which the code under test does import ldap
, use
the LDAPFakerMixin.ldap_modules
class attribute.
For example, if you have a class MyLDAPUsingClass
in the module
myapp.myldapstuff
, and you do import ldap
in myapp.myldapstuff
, for
instance:
import ldap
class MyLDAPUsingClass:
def connect(self, uid: str, password: str):
self.conn = ldap.initialize('ldap://server')
self.conn.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
self.conn.start_tls_s()
self.conn.simple_bind_s(
f'uid={uid},ou=bar,o=baz,c=country',
'the password'
)
To test this code, you would use this for ldap_modules
:
import unittest
from ldap_faker import LDAPFakerMixin
from myapp.myldapstuff import MyLDAPUsingClass
class TestMyLDAPUsingCLass(LDAPFakerMixin, unittest.TestCase):
ldap_modules = ['myapp.myldapstuff']
LDAPFakerMixin.ldap_fixtures
In order to effectively test your python-ldap
using code, you’ll need to
populate an LDAPServerFactory
one or more ObjectStore
objects bound to LDAP URIs. We use LDAPFakerMixin.ldap_fixtures
to
declare file paths to fixture files to use to populate those
ObjectClass
objects.
Fixture files are JSON files in the format described in File format for ObjectStore.load_objects.
File paths are either absolute paths or are treated as relative to the folder in which your
TestCase
resides.Fixtures are loaded into the
LDAPServerFactory
once perunittest.TestCase
via theunittest.TestCase.setUpClass
classmethod.
You can configure your LDAPFakerMixin
to use fixtures one of two ways:
Use a single default fixture that will be used no matter which LDAP URI is passed to
FakeLDAP.initialize
Bind each fixture to specific a LDAP URI. This allows you simulate talking to several different LDAP servers.
Note
When binding fixtures to particular LDAP URIs, if your tries to use
FakeLDAP.initialize
with an LDAP URI that was not explicitly configured,
python-ldap-faker
will raise ldap.SERVER_DOWN
This form sets up one default fixture:
import unittest
from ldap_faker import LDAPFakerMixin
from myapp.myldapstuff import MyLDAPUsingClass
class TestMyLDAPUsingCLass(LDAPFakerMixin, unittest.TestCase):
ldap_fixtures = 'objects.json'
This form binds fixtures to LDAP URIs:
import unittest
from ldap_faker import LDAPFakerMixin
from myapp.myldapstuff import MyLDAPUsingClass
class TestMyLDAPUsingCLass(LDAPFakerMixin, unittest.TestCase):
ldap_fixtures = [
('server1.json', 'ldap://server1.example.com'),
('server2.json', 'ldap://server2.example.com')
]
Test isolation
Each test method on your unittest.TestCase
will get a fresh, unaltered
copy of the fixture data, and connections, call histories, options set from previous
test methods will be cleared.
Test support offered by LDAPFakerMixin
For each test you run, your test will have access to the FakeLDAP
instance used for that test through the LDAPFakerMixin.fake_ldap
instance attribute. Each test gets a fresh FakeLDAP
instance.
Note
For detailed information on any of the below, see the Developer Interface.
Some things to know about your FakeLDAP
instance:
FakeLDAP.connections
lists all theFakeLDAPObject
connections created during your test method, in the order they were made. One such object is created each timeFakeLDAP.initialize
is called by your code.FakeLDAP.options
is aOptionStore
object that records all the global LDAP options set during your testFakeLDAP.calls
is aCallHistory
object that records calls (with arguments) toFakeLDAP.initialize
,FakeLDAP.set_option
,FakeLDAP.get_option
Some things to know about the FakeLDAPObject
objects in
FakeLDAP.connections
:
FakeLDAPObject.uri
is the LDAP URI requestedFakeLDAPObject.store
is ourObjectStore
copyFakeLDAP.options
is aOptionStore
object that records all the LDAP options set on this connection during your test methodFakeLDAPObject.calls
is aCallHistory
that records allpython-ldap
api calls (with arguments) that your code made to thisFakeLDAPObject
FakeLDAPObject.bound_dn
is thedn
of the user bound viasimple_bind_s
, if any. If this isNone
, we did anonymous binding.FakeLDAPObject.tls_enabled
will be set toTrue
ifstart_tls_s
was used on this connection