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
ObjectStoreobjects (our fake LDAP server class)Associate those
ObjectStoreobjects with particular LDAP URIsPatch
ldap.initializeto returnFakeLDAPObjectobjects configured with the appropriateObjectStorefor 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_optionandldap.get_option`LDAPFakerMixin.ldap_fixtures: A list of JSON fixture files with which to create theObjectStoreobjects
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
TestCaseresides.Fixtures are loaded into the
LDAPServerFactoryonce perunittest.TestCasevia theunittest.TestCase.setUpClassclassmethod.
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.initializeBind 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.connectionslists all theFakeLDAPObjectconnections created during your test method, in the order they were made. One such object is created each timeFakeLDAP.initializeis called by your code.FakeLDAP.optionsis aOptionStoreobject that records all the global LDAP options set during your testFakeLDAP.callsis aCallHistoryobject 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.uriis the LDAP URI requestedFakeLDAPObject.storeis ourObjectStorecopyFakeLDAP.optionsis aOptionStoreobject that records all the LDAP options set on this connection during your test methodFakeLDAPObject.callsis aCallHistorythat records allpython-ldapapi calls (with arguments) that your code made to thisFakeLDAPObjectFakeLDAPObject.bound_dnis thednof the user bound viasimple_bind_s, if any. If this isNone, we did anonymous binding.FakeLDAPObject.tls_enabledwill be set toTrueifstart_tls_swas used on this connection