# python-ldap-faker
**Documentation**: https://python-ldap-faker.readthedocs.org
Fakes are objects that have working implementations. while mocks are objects that have predefined behavior. `python-ldap-faker` provides a fake `python-ldap` interface and "server" that can be used for automated testing of code that uses `python-ldap`.
Managing an actual LDAP server during our tests is usually out of the question, so typically we revert to patching the `python-ldap` code to use mock objects instead, but this is very verbose and can lead to test code errors in practice.
## Installation
`python-ldap-faker` supports Python 3.7+.
To install from PyPI:
```shell
pip install python-ldap-faker
```
If you want, you can run the tests:
```shell
python -m unittest discover
```
## Features
* These `python-ldap` global functions are faked:
* `ldap.initialize`
* `ldap.set_option`
* `ldap.get_option`
* These `ldap.ldapobject.LDAPObject` methods are faked:
* `set_option`
* `get_option`
* `start_tls_s`
* `simple_bind_s`
* `unbind_s`
* `search_s`
* `search_ext`
* `result3`
* `compare_s`
* `add_s`
* `modify_s`
* `rename_s`
* `delete_s`
* For `search_ext` and `search_s`, your filter string will be validated as a valid LDAP filter, and your filter will be applied directly to your objects in our fake "server" to generate the result list. No canned searches!
* Inspect your call history for all calls (name, arguments), and test the order in which they were made
* Simulate multiple fake LDAP "servers" with different sets of objects that correspond to different LDAP URIs.
* Ease your test setup with :py:class:`LDAPFakerMixin`, a mixin for :py:class:`unittest.TestCase`
* Automatically manages patching `python-ldap` for the code under test
* Allows you to populate objects into one or more LDAP "servers" with fixture files
* Provides the following test instrumentation for inspecting state after the test:
* Access to the full object store for each LDAP uri accessed
* All connections made
* All `python-ldap` API calls made
* All `python-ldap` LDAP options set
* Provides test isolation: object store changes, connections, call history, option changes are all reset between tests
* Use handy LDAP specific asserts to ease your testing
* Define your own hooks to change the behavior of your fake "servers"
* Support behavior for specific LDAP implementations:
* Redhat Directory Server/389 implementation support: have your test believe it's talking to an RHDS/389 server.
## Quickstart
The easiest way to use `python-ldap-faker` in your `unittest` based tests is to use the `ldap_faker.LDAPFakerMixin` mixin for `unittest.TestCase`.
This will patch `ldap.initialize`, `ldap.set_option` and `ldap.get_option` to use our `FakeLDAP` interface, and load fixtures in from JSON files to use as test data.
Let's say we have a class `App` in our `myapp` module that does LDAP work that we want to test.
First, prepare a file named `data.json` with the objects you want loaded into your fake LDAP server. Let's say you want your data to consist of some `posixAccount` objects. If we make `data.json` look like this:
```json
[
[
"uid=foo,ou=bar,o=baz,c=country",
{
"uid": ["foo"],
"cn": ["Foo Bar"],
"uidNumber": ["123"],
"gidNumber": ["123"],
"homeDirectory": ["/home/foo"],
"userPassword": ["the password"],
"objectclass": [
"posixAccount",
"top"
]
}
],
[
"uid=fred,ou=bar,o=baz,c=country",
{
"uid": ["fred"],
"cn": ["Fred Flintstone"],
"uidNumber": ["124"],
"gidNumber": ["124"],
"homeDirectory": ["/home/fred"],
"userPassword": ["the fredpassword"],
"objectclass": [
"posixAccount",
"top"
]
}
],
[
"uid=barney,ou=bar,o=baz,c=country",
{
"uid": ["barney"],
"cn": ["Barney Rubble"],
"uidNumber": ["125"],
"gidNumber": ["125"],
"homeDirectory": ["/home/barney"],
"userPassword": ["the barneypassword"],
"objectclass": [
"posixAccount",
"top"
]
}
]
]
```
Then we can write a `TestCase` that looks like this:
```python
import unittest
import ldap
from ldap_faker import LDAPFakerMixin
from myapp import App
class YourTestCase(LDAPFakerMixin, unittest.TestCase):
ldap_modules = ['myapp']
ldap_fixtures = 'data.json'
def test_auth_works(self):
app = App()
# A method that does a `simple_bind_s`
app.auth('fred', 'the fredpassword')
conn = self.get_connections()[0]
self.assertLDAPConnectionMethodCalled(
conn, 'simple_bind_s',
{'who': 'uid=fred,ou=bar,o=baz,c=country', 'cred': 'the fredpassword'}
)
def test_correct_connection_options_were_set(self):
app = App()
app.auth('fred', 'the fredpassword')
conn = self.get_connections()[0]
self.assertLDAPConnectionOptionSet(conn, ldap.OPT_X_TLX_NEWCTX, 0)
def test_tls_was_used_before_auth(self):
app = App()
app.auth('fred', 'the fredpassword')
conn = self.get_connections()[0]
self.assertLDAPConnectiontMethodCalled(conn, 'start_tls_s')
self.assertLDAPConnectionMethodCalledAfter(conn, 'simple_bind_s', 'start_tls_s')
```
Raw data
{
"_id": null,
"home_page": null,
"name": "python-ldap-faker",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "ldap, testing",
"author": null,
"author_email": "Caltech IMSS ADS <cmalek@caltech.edu>",
"download_url": "https://files.pythonhosted.org/packages/e9/b2/4e65ad176bded46ba231da20633f27c3663df5dc6a2c983bab60a6cbf001/python_ldap_faker-1.3.4.tar.gz",
"platform": null,
"description": "# python-ldap-faker\n\n**Documentation**: https://python-ldap-faker.readthedocs.org\n\nFakes are objects that have working implementations. while mocks are objects that have predefined behavior. `python-ldap-faker` provides a fake `python-ldap` interface and \"server\" that can be used for automated testing of code that uses `python-ldap`.\n\nManaging an actual LDAP server during our tests is usually out of the question, so typically we revert to patching the `python-ldap` code to use mock objects instead, but this is very verbose and can lead to test code errors in practice.\n\n## Installation\n\n`python-ldap-faker` supports Python 3.7+.\n\nTo install from PyPI:\n\n```shell\npip install python-ldap-faker\n```\n\nIf you want, you can run the tests:\n\n```shell\npython -m unittest discover\n```\n\n## Features\n\n* These `python-ldap` global functions are faked:\n\n * `ldap.initialize`\n * `ldap.set_option`\n * `ldap.get_option`\n\n* These `ldap.ldapobject.LDAPObject` methods are faked:\n\n * `set_option`\n * `get_option`\n * `start_tls_s`\n * `simple_bind_s`\n * `unbind_s`\n * `search_s`\n * `search_ext`\n * `result3`\n * `compare_s`\n * `add_s`\n * `modify_s`\n * `rename_s`\n * `delete_s`\n\n* For `search_ext` and `search_s`, your filter string will be validated as a valid LDAP filter, and your filter will be applied directly to your objects in our fake \"server\" to generate the result list. No canned searches!\n* Inspect your call history for all calls (name, arguments), and test the order in which they were made\n* Simulate multiple fake LDAP \"servers\" with different sets of objects that correspond to different LDAP URIs.\n* Ease your test setup with :py:class:`LDAPFakerMixin`, a mixin for :py:class:`unittest.TestCase`\n\n * Automatically manages patching `python-ldap` for the code under test\n * Allows you to populate objects into one or more LDAP \"servers\" with fixture files\n * Provides the following test instrumentation for inspecting state after the test:\n\n * Access to the full object store for each LDAP uri accessed\n * All connections made\n * All `python-ldap` API calls made\n * All `python-ldap` LDAP options set\n\n * Provides test isolation: object store changes, connections, call history, option changes are all reset between tests\n * Use handy LDAP specific asserts to ease your testing\n\n* Define your own hooks to change the behavior of your fake \"servers\"\n* Support behavior for specific LDAP implementations:\n\n * Redhat Directory Server/389 implementation support: have your test believe it's talking to an RHDS/389 server.\n\n## Quickstart\n\nThe easiest way to use `python-ldap-faker` in your `unittest` based tests is to use the `ldap_faker.LDAPFakerMixin` mixin for `unittest.TestCase`.\n\nThis will patch `ldap.initialize`, `ldap.set_option` and `ldap.get_option` to use our `FakeLDAP` interface, and load fixtures in from JSON files to use as test data.\n\nLet's say we have a class `App` in our `myapp` module that does LDAP work that we want to test.\n\nFirst, prepare a file named `data.json` with the objects you want loaded into your fake LDAP server. Let's say you want your data to consist of some `posixAccount` objects. If we make `data.json` look like this:\n\n```json\n[\n [\n \"uid=foo,ou=bar,o=baz,c=country\",\n {\n \"uid\": [\"foo\"],\n \"cn\": [\"Foo Bar\"],\n \"uidNumber\": [\"123\"],\n \"gidNumber\": [\"123\"],\n \"homeDirectory\": [\"/home/foo\"],\n \"userPassword\": [\"the password\"],\n \"objectclass\": [\n \"posixAccount\",\n \"top\"\n ]\n }\n ],\n [\n \"uid=fred,ou=bar,o=baz,c=country\",\n {\n \"uid\": [\"fred\"],\n \"cn\": [\"Fred Flintstone\"],\n \"uidNumber\": [\"124\"],\n \"gidNumber\": [\"124\"],\n \"homeDirectory\": [\"/home/fred\"],\n \"userPassword\": [\"the fredpassword\"],\n \"objectclass\": [\n \"posixAccount\",\n \"top\"\n ]\n }\n ],\n [\n \"uid=barney,ou=bar,o=baz,c=country\",\n {\n \"uid\": [\"barney\"],\n \"cn\": [\"Barney Rubble\"],\n \"uidNumber\": [\"125\"],\n \"gidNumber\": [\"125\"],\n \"homeDirectory\": [\"/home/barney\"],\n \"userPassword\": [\"the barneypassword\"],\n \"objectclass\": [\n \"posixAccount\",\n \"top\"\n ]\n }\n ]\n]\n```\n\nThen we can write a `TestCase` that looks like this:\n\n```python\n import unittest\n\n import ldap\n from ldap_faker import LDAPFakerMixin\n\n from myapp import App\n\n class YourTestCase(LDAPFakerMixin, unittest.TestCase):\n\n ldap_modules = ['myapp']\n ldap_fixtures = 'data.json'\n\n def test_auth_works(self):\n app = App()\n # A method that does a `simple_bind_s`\n app.auth('fred', 'the fredpassword')\n conn = self.get_connections()[0]\n self.assertLDAPConnectionMethodCalled(\n conn, 'simple_bind_s',\n {'who': 'uid=fred,ou=bar,o=baz,c=country', 'cred': 'the fredpassword'}\n )\n\n def test_correct_connection_options_were_set(self):\n app = App()\n app.auth('fred', 'the fredpassword')\n conn = self.get_connections()[0]\n self.assertLDAPConnectionOptionSet(conn, ldap.OPT_X_TLX_NEWCTX, 0)\n\n def test_tls_was_used_before_auth(self):\n app = App()\n app.auth('fred', 'the fredpassword')\n conn = self.get_connections()[0]\n self.assertLDAPConnectiontMethodCalled(conn, 'start_tls_s')\n self.assertLDAPConnectionMethodCalledAfter(conn, 'simple_bind_s', 'start_tls_s')\n```\n",
"bugtrack_url": null,
"license": null,
"summary": "Fake python-ldap functions, objects and methods for use in testing.",
"version": "1.3.4",
"project_urls": {
"Documentation": "https://python-ldap-faker.readthedocs.io/en/latest/",
"Homepage": "https://github.com/caltechads/python-ldap-faker",
"Issues": "https://github.com/caltechads/python-ldap-faker/issues",
"Source": "https://github.com/caltechads/python-ldap-faker"
},
"split_keywords": [
"ldap",
" testing"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "7c765584e6d271fee8eae306f52d210e55b301d240dbce5f0e0d5be2f9022b6d",
"md5": "4c0d9f6c452978e0df4e42bea2116d76",
"sha256": "60685cbbd8488eb8f3e08bc0df67133d7b50861be627614a94586bc69d640bcf"
},
"downloads": -1,
"filename": "python_ldap_faker-1.3.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "4c0d9f6c452978e0df4e42bea2116d76",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 93860,
"upload_time": "2025-07-25T00:56:43",
"upload_time_iso_8601": "2025-07-25T00:56:43.373689Z",
"url": "https://files.pythonhosted.org/packages/7c/76/5584e6d271fee8eae306f52d210e55b301d240dbce5f0e0d5be2f9022b6d/python_ldap_faker-1.3.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "e9b24e65ad176bded46ba231da20633f27c3663df5dc6a2c983bab60a6cbf001",
"md5": "7349069120ed9b523b5d0560fa45332d",
"sha256": "cda7f01ef2f5171508c171f05b532b39ca54be78d7cdb6040ef7d0757d16eff8"
},
"downloads": -1,
"filename": "python_ldap_faker-1.3.4.tar.gz",
"has_sig": false,
"md5_digest": "7349069120ed9b523b5d0560fa45332d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 80950,
"upload_time": "2025-07-25T00:56:44",
"upload_time_iso_8601": "2025-07-25T00:56:44.458328Z",
"url": "https://files.pythonhosted.org/packages/e9/b2/4e65ad176bded46ba231da20633f27c3663df5dc6a2c983bab60a6cbf001/python_ldap_faker-1.3.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-25 00:56:44",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "caltechads",
"github_project": "python-ldap-faker",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "alabaster",
"specs": [
[
"==",
"0.7.16"
]
]
},
{
"name": "asn1",
"specs": [
[
"==",
"3.1.0"
]
]
},
{
"name": "babel",
"specs": [
[
"==",
"2.17.0"
]
]
},
{
"name": "case-insensitive-dictionary",
"specs": [
[
"==",
"0.2.1"
]
]
},
{
"name": "certifi",
"specs": [
[
"==",
"2025.7.14"
]
]
},
{
"name": "charset-normalizer",
"specs": [
[
"==",
"3.4.2"
]
]
},
{
"name": "coverage",
"specs": [
[
"==",
"7.9.2"
]
]
},
{
"name": "docutils",
"specs": [
[
"==",
"0.17.1"
]
]
},
{
"name": "enum-compat",
"specs": [
[
"==",
"0.0.3"
]
]
},
{
"name": "idna",
"specs": [
[
"==",
"3.10"
]
]
},
{
"name": "imagesize",
"specs": [
[
"==",
"1.4.1"
]
]
},
{
"name": "iniconfig",
"specs": [
[
"==",
"2.1.0"
]
]
},
{
"name": "jinja2",
"specs": [
[
"==",
"3.1.6"
]
]
},
{
"name": "ldap-filter",
"specs": [
[
"==",
"1.0.1"
]
]
},
{
"name": "markupsafe",
"specs": [
[
"==",
"3.0.2"
]
]
},
{
"name": "nose",
"specs": [
[
"==",
"1.3.7"
]
]
},
{
"name": "packaging",
"specs": [
[
"==",
"25.0"
]
]
},
{
"name": "pluggy",
"specs": [
[
"==",
"1.6.0"
]
]
},
{
"name": "pyasn1",
"specs": [
[
"==",
"0.6.1"
]
]
},
{
"name": "pyasn1-modules",
"specs": [
[
"==",
"0.4.2"
]
]
},
{
"name": "pygments",
"specs": [
[
"==",
"2.19.2"
]
]
},
{
"name": "pytest",
"specs": [
[
"==",
"8.4.1"
]
]
},
{
"name": "python-ldap",
"specs": [
[
"==",
"3.4.4"
]
]
},
{
"name": "requests",
"specs": [
[
"==",
"2.32.4"
]
]
},
{
"name": "snowballstemmer",
"specs": [
[
"==",
"3.0.1"
]
]
},
{
"name": "sphinx",
"specs": [
[
"==",
"5.2.3"
]
]
},
{
"name": "sphinx-rtd-theme",
"specs": [
[
"==",
"1.0.0"
]
]
},
{
"name": "sphinxcontrib-applehelp",
"specs": [
[
"==",
"2.0.0"
]
]
},
{
"name": "sphinxcontrib-devhelp",
"specs": [
[
"==",
"2.0.0"
]
]
},
{
"name": "sphinxcontrib-htmlhelp",
"specs": [
[
"==",
"2.1.0"
]
]
},
{
"name": "sphinxcontrib-jsmath",
"specs": [
[
"==",
"1.0.1"
]
]
},
{
"name": "sphinxcontrib-qthelp",
"specs": [
[
"==",
"2.0.0"
]
]
},
{
"name": "sphinxcontrib-serializinghtml",
"specs": [
[
"==",
"2.0.0"
]
]
},
{
"name": "urllib3",
"specs": [
[
"==",
"2.5.0"
]
]
}
],
"tox": true,
"lcname": "python-ldap-faker"
}