{"id":15374794,"url":"https://github.com/mondeja/pytest-blender","last_synced_at":"2026-04-18T16:07:56.753Z","repository":{"id":37795315,"uuid":"338848269","full_name":"mondeja/pytest-blender","owner":"mondeja","description":"Pytest plugin for easy addons testing with Blender's Python interpreter","archived":false,"fork":false,"pushed_at":"2025-01-11T18:28:31.000Z","size":124,"stargazers_count":36,"open_issues_count":2,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-06T01:43:47.708Z","etag":null,"topics":["blender","blender-python","pytest-blender","pytest-plugin"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mondeja.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-14T16:28:11.000Z","updated_at":"2025-02-17T10:11:45.000Z","dependencies_parsed_at":"2024-06-12T18:15:16.644Z","dependency_job_id":"1d7496a7-cad5-442f-a076-d18cd43dbad8","html_url":"https://github.com/mondeja/pytest-blender","commit_stats":{"total_commits":102,"total_committers":6,"mean_commits":17.0,"dds":0.3137254901960784,"last_synced_commit":"c14a2c3424c1ed40254689ff525fd8ee328c3241"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mondeja%2Fpytest-blender","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mondeja%2Fpytest-blender/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mondeja%2Fpytest-blender/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mondeja%2Fpytest-blender/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mondeja","download_url":"https://codeload.github.com/mondeja/pytest-blender/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247595334,"owners_count":20963943,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blender","blender-python","pytest-blender","pytest-plugin"],"created_at":"2024-10-01T13:59:47.167Z","updated_at":"2026-04-18T16:07:56.743Z","avatar_url":"https://github.com/mondeja.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pytest-blender\n\n[![PyPI][pypi-version-badge-link]][pypi-link]\n[![Python versions][pypi-pyversions-badge-link]][pypi-link]\n[![License][license-image]][license-link]\n[![Tests][tests-image]][tests-link]\n\nPytest plugin for Blender testing. Executes your pytest testsuite with\nBlender in headless mode using its builtin Python interpreter.\n\n## Install\n\n```sh\npip install pytest-blender\n```\n\n## Documentation\n\n### Usage\n\nBefore execute it, you need to install your testing dependencies inside the\nbuiltin Blender Python interpreter. To get the interpreter location you can\nuse the CLI utility `pytest-blender`, something like:\n\n```sh\nblender_python=\"$(pytest-blender)\"\n$blender_python -m ensurepip\n$blender_python -m pip install -r test-requirements.txt\n```\n\nAfter installing dependencies, just call pytest as usually.\n\n```sh\npytest -svv\n```\n\n```\nBlender 2.82 (sub 7)\nRead prefs: ~/.config/blender/2.82/config/userpref.blend\n=================== test session starts ===================\nplatform linux -- Python 3.8.5, pytest-6.1.2, py-1.9.0, pluggy-0.13.1 -- /usr/bin/blender\ncachedir: .pytest_cache\nrootdir: /home/mondeja/files/code/pytest-blender\ncollected 1 item\n\ntests/test_bpy_import.py::test_inside_blender \u003cmodule 'bpy' from '/usr/share/blender/scripts/modules/bpy/__init__.py'\u003e\nPASSED\n==================== 1 passed in 0.01s ====================\n```\n\n### Reference\n\n#### Configuration\n\nAll options can be passed as a CLI argument like `--[option-name]` or\ndefined inside a [configuration file][pytest-configuration].\n\n##### `blender-executable`\n\nSpecify a custom `blender` executable location.\n\n```sh\npytest --blender-executable ~/blender-2.91.2-linux64/blender\n```\n\n```ini\n[pytest]\nblender-executable = ~/blender-2.91.2-linux64/blender\n```\n\n```\nBlender 2.91.2 (hash 5be9ef417703 built 2021-01-19 16:16:34)\nRead prefs: ~/.config/blender/2.91/config/userpref.blend\nfound bundled python: ~/blender-2.91.2-linux64/2.91/python\n=================== test session starts ===================\nplatform linux -- Python 3.7.7, pytest-6.2.2, py-1.10.0, pluggy-0.13.1\nrootdir: ~/pytest-blender\ncollected 1 item\n\ntests/test_bpy_import.py .                                                [100%]\n\n==================== 1 passed in 0.00s ====================\n```\n\n##### `blender-template`\n\nLoad a custom startup `.blend` template.\n\n```sh\npytest -svv --blender-template ~/.config/blender/2.93/config/startup.blend\n```\n\n```ini\n[pytest]\nblender-template = ~/.config/blender/2.93/config/startup.blend\naddopts = -svv\n```\n\n##### `blender-addons-dirs`\n\nInstall addons inside Blender before executing the test suite. This allows\nyou to easily test them.\n\nBy \"addons\" Blender understands Python scripts whose file names\nend with `.py`, `.zip` files for compressed packages with multiple modules\nor directories for Python packages which contain a `__init__.py` file.\nThese must be located in the root of each directory passed to\n`blender-addons-dirs`.\n\nFor example, given the next directory tree:\n\n```tree\n📁 addons-dirs\n├── 📁 private-addons\n│   └── 📁 package_addon\n│       ├── 📄 __init__.py \n│       └── 📄 main.py\n|\n└── 📁 public-addons\n    ├── 📄 module_addon.py\n    └── 📄 compressed_addon.zip\n        ├── 📄 __init__.py \n        └── 📄 main.py\n```\n\nThe next configurations will install the addons `package_addon`,\n`module_addon` and `compressed_addon`.\n\n```sh\npytest tests --blender-addons-dirs addons-dirs/private-addons addons-dirs/public-addons\n```\n\n```ini\n[pytest]\nblender-addons-dirs =\n    addons-dirs/private-addons\n    addons-dirs/public-addons\n```\n\nYou can also define a unique addons directory in configuration files\ndefining it as a string:\n\n```ini\n[pytest]\nblender-addons-dirs = addons-dirs/public-addons\n```\n\nIf you need more complex setups see the fixtures\n[`install_addons_from_dir`](#install_addons_from_dir),\n[`disable_addons`](#disable_addons) and\n[`uninstall_addons`](#uninstall_addons).\n\n##### `blender-addons-cleaning`\n\nDefine the addons cleaning strategy to follow after executing your\ntest suite. It only affects to the addons installed using\n[`blender-addons-dirs`](#blender-addons-dirs).\n\nIt accepts one of the next values:\n\n- `uninstall` (default): Uninstall the addons after executing the\n test suite.\n- `disable`: Just disable the addons in user preferences, but does\n not uninstall them.\n- `keep`: Keep the addons enabled. Useful if you want to manually\n review the addons or while you're developing. \n\n```sh\npytest --blender-addons-cleaning disable\n```\n\n```ini\n[pytest]\nblender-addons-cleaning = disable\n```\n\n##### `pytest-blender-debug`\n\nShow in STDOUT the command executed by pytest-blender executing your\ntest suite.\n\n```sh\npytest --pytest-blender-debug\n```\n\n```ini\n[pytest]\npytest-blender-debug = true\n```\n\n```\n[DEBUG (pytest-blender)] Running blender with: /usr/bin/blender -b --python /home/foo/files/code/pytest-blender/pytest_blender/run_pytest.py -- --pytest-blender-executable /usr/bin/blender -svv --rootdir=/tmp/tmpdsh0wnsf --strict-markers --strict-config -c /tmp/tmpdsh0wnsf/pytest.ini\nBlender 2.82 (sub 7)\nRead prefs: /home/foo/.config/blender/2.82/config/userpref.blend\n=================== test session starts ===================\nplatform linux -- Python 3.8.10, pytest-7.0.1, pluggy-0.13.1 -- /usr/bin/blender\ncachedir: .pytest_cache\nrootdir: /tmp/tmpyio7hlc2, configfile: pytest.ini\nplugins: cov-3.0.0, Faker-12.1.0\ncollecting ... collected 1 item\n\ntests/test_foo.py::test_foo PASSED\n\n==================== 1 passed in 0.09s ====================\n```\n\n#### Fixtures\n\n\u003ca name=\"blender_executable\" href=\"#blender_executable\"\u003e#\u003c/a\u003e\n\u003cb\u003eblender_executable\u003c/b\u003e ⇒ `str`\n\nReturns the path of the executable that has started the current Blender\nsession.\n\n\u003ca name=\"blender_version\" href=\"#blender_version\"\u003e#\u003c/a\u003e \u003cb\u003eblender_version\u003c/b\u003e\n⇒ `str`\n\nReturns the version of Blender running in the current session.\n\n\u003ca name=\"blender_python_executable\" href=\"#blender_python_executable\"\u003e#\u003c/a\u003e\n\u003cb\u003eblender_python_executable\u003c/b\u003e ⇒ `str`\n\nReturns the path of the Python executable builtin in the Blender release of the\ncurrently running session.\n\n\u003ca name=\"blender_python_version\" href=\"#blender_python_version\"\u003e#\u003c/a\u003e\n\u003cb\u003eblender_python_version\u003c/b\u003e ⇒ `str`\n\nReturns the version of the Python executable builtin in the Blender release of\nthe currently running session.\n\n\u003ca name=\"blender_addons_dir\" href=\"#blender_addons_dir\"\u003e#\u003c/a\u003e\n\u003cb\u003eblender_addons_dir\u003c/b\u003e ⇒ `str`\n\nReturns the `scripts/addons` directory of Blender (see\n[Blender Directory Layout]), the directory in which by default are located\nthe addons installed using the\n[`install_addons_from_dir`](#install_addons_from_dir) fixture.\n\nIt tries to get it using the `BLENDER_USER_SCRIPTS` environment variable, but\nif is not defined attempts to discover it from the `PATH`.\n\n\u003ca name=\"install_addons_from_dir\" href=\"#install_addons_from_dir\"\u003e#\u003c/a\u003e\n\u003cb\u003einstall_addons_from_dir\u003c/b\u003e(\u003ci\u003eaddons_dir\u003c/i\u003e, \u003ci\u003eaddon_ids=None\u003c/i\u003e,\n\u003ci\u003esave_userpref=True\u003c/i\u003e, \u003ci\u003edefault_set=True\u003c/i\u003e, \u003ci\u003epersistent=True\u003c/i\u003e,\n\u003ci\u003equiet=True\u003c/i\u003e, \u003ci\u003e\\*\\*kwargs\u003c/i\u003e) ⇒ `list`\n\nFunction that installs and enables a set of addons which are located in\na directory. By \"addons\" Blender understands Python scripts whose file names\nend with `.py`, `.zip` files for compressed packages with multiple modules\nor directories for Python packages which contain a `__init__.py` file.\n\nThis function is designed to be executed before the pytest session\nto install the addons that you want to test, using the others fixtures\n[`disable_addons`](#disable_addons) or [`uninstall_addons`](#uninstall_addons)\nto disable or remove them after the execution of the test suite:\n\n```python\nimport pytest\n\n@pytest.fixture(scope=\"session\", autouse=True)\ndef register_addons(install_addons_from_dir, disable_addons):\n    addons_ids = install_addons_from_dir(\"src\")\n    yield\n    disable_addons(addons_ids)\n```\n\n```python\nimport pytest\n\n@pytest.fixture(scope=\"session\", autouse=True)\ndef register_addons(install_addons_from_dir, uninstall_addons):\n    addons_ids = install_addons_from_dir(\"src\")\n    yield\n    uninstall_addons(addons_ids)\n```\n\nThe difference between disabling addons and uninstalling them is that disabling\nremoves the files from the Blender's addons directory but disabling keep the\nfiles there, allowing you to enable it manually from the preferences.\n\n- **addons_dir** (str) Directory in whose root are located the files of the\n addons.\n- **addons_ids** (list) Identifiers of the addons modules, packages or ZIP\n files (without extensions) to install. If not defined (default) all Python\n modules, Python packages and ZIP files containing addon packages or modules\n located at the root of the `addons_dir` directory will be installed.\n These identifiers are either:\n  - The name of the module for addons composed by a single file\n   (`[identifier].py`).\n  - The name of the directory for addons composed by a package.\n  - The name of the ZIP file without extension for addons composed by a\n   ZIP file (`[identifier].zip`).\n- **save_userpref** (bool) Save user preferences after installation calling\n [`bpy.ops.wm.save_userpref`]\n- **default_set** (bool) Set the user-preference calling `addon_utils.enable`.\n- **persistent** (bool) Ensure that the addon is enabled for the entire\n session, after loading new files.\n - **quiet** (bool) If enabled, don't show standard output produced\n installing addons.\n- **\\*\\*kwargs** (dict) Subsecuent keyword arguments are passed to\n [`bpy.ops.preferences.addon_install`].\n\nReturns the addons identifiers as a list, ready to be passed to\n[`disable_addons`](#disable_addons) or [`uninstall_addons`](#uninstall_addons).\n\n\u003ca name=\"disable_addons\" href=\"#disable_addons\"\u003e#\u003c/a\u003e\n\u003cb\u003edisable_addons\u003c/b\u003e(\u003ci\u003eaddons_ids\u003c/i\u003e, \u003ci\u003esave_userpref=True\u003c/i\u003e,\n\u003ci\u003edefault_set=True\u003c/i\u003e, \u003ci\u003equiet=True\u003c/i\u003e, \u003ci\u003e\\*\\*kwargs\u003c/i\u003e)\n\nFunction that disables a set of addons by addons identifiers. Is designed\nto disable your addons after a pytest suite execution (check\n[`install_addons_from_dir`](#install_addons_from_dir) for an example).\n\n- **addons_ids** (list) Identifiers of the addons modules as are returned by\n [`install_addons_from_dir`](#install_addons_from_dir).\n- **save_userpref** (bool) Save user preferences after installation.\n- **default_set** (bool) Set the user-preference calling `addon_utils.disable`.\n- **quiet** (bool) If enabled, don't show stdout produced disabling addons.\n- **\\*\\*kwargs** (dict) Subsecuent keyword arguments are passed to \n `addon_utils.disable`.\n\n\u003ca name=\"uninstall_addons\" href=\"#uninstall_addons\"\u003e#\u003c/a\u003e\n\u003cb\u003euninstall_addons\u003c/b\u003e(\u003ci\u003eaddons_ids\u003c/i\u003e, \u003ci\u003equiet=True\u003c/i\u003e)\n\nFunction that uninstall a set of addons by addon identifiers. Is designed to\nremove your addons from the Blender's addons directory after a pytest suite\nexecution (check [`install_addons_from_dir`](#install_addons_from_dir)\nfor an example).\n\n- **addons_ids** (list) Name of the addons modules as is returned by\n [`install_addons_from_dir`](#install_addons_from_dir).\n- **quiet** (bool) If enabled, don't show stdout produced disabling addons.\n\n### Arguments propagation\n\nWhen you call `pytest`, all options like `--blender-executable` are passed\nto the `pytest` suite running `pytest-blender`. If you want to pass arguments\nto `blender` in its headless execution, add a `--` between `pytest` and\n`blender` arguments.\nFor example:\n\n```sh\npytest -svv --blender-executable ~/blender -- --debug\n```\n\n### Measuring code coverage\n\nYou can use [pytest-cov] to measure code coverage. If so, instead of\nusing [`blender-addons-dirs`](#blender-addons-dirs) configuration option\nuse [pytest's `pythonpath` option].\n\nExample for *pytest.ini*:\n\n```ini\n[pytest]\npythonpath = path/to/blender/addons\naddopts = --cov path/to/blender/addons\n```\n\n### CI integration\n\nYou can use [blender-downloader] to download multiple\nversions of Blender in your CI and test against them. There is an example\nfor Github Actions in the CI configuration of this repository, something\nlike:\n\n```yaml\njobs:\n  test:\n    name: Test\n    runs-on: ${{ matrix.platform }}\n    strategy:\n      matrix:\n        platform:\n          - ubuntu-latest\n          - macos-latest\n        blender-version:\n          - '3.1.2'\n          - '2.93.9'\n          - '2.83.9'\n    steps:\n      - uses: actions/checkout@v3\n      - name: Set up Python v3.9\n        uses: actions/setup-python@v3\n        with:\n          python-version: 3.9\n      - name: Upgrade PIP\n        run: python -m pip install --upgrade pip\n      - name: Cache Blender ${{ matrix.blender-version }}\n        uses: actions/cache@v3\n        id: cache-blender\n        with:\n          path: |\n            blender-*\n            _blender-executable-path.txt\n          key: ${{ runner.os }}-${{ matrix.blender-version }}\n      - name: Download Blender ${{ matrix.blender-version }}\n        if: steps.cache-blender.outputs.cache-hit != 'true'\n        id: download-blender\n        run: |\n          python -m pip install --upgrade blender-downloader\n          printf \"%s\" \"$(blender-downloader \\\n          ${{ matrix.blender-version }} --extract --remove-compressed \\\n          --quiet --print-blender-executable)\" \u003e _blender-executable-path.txt\n      - name: Install dependencies\n        id: install-dependencies\n        run: |\n          python -m pip install .[test]\n          blender_executable=\"$(\u003c _blender-executable-path.txt)\"\n          python_blender_executable=\"$(pytest-blender --blender-executable $blender_executable)\"\n          $python_blender_executable -m ensurepip\n          $python_blender_executable -m pip install pytest\n          echo \"blender-executable=$BLENDER_EXECUTABLE\" \u003e\u003e $GITHUB_OUTPUT\n      - name: Test with pytest\n        run: pytest -svv --blender-executable \\\n          \"${{ steps.install-dependencies.outputs.blender-executable }}\" tests\n```\n\n### Versions compatibility\n\n- Latest version that officially supports Python3.6 is [v1.2.1].\n\n\n[pypi-link]: https://pypi.org/project/pytest-blender\n[pypi-version-badge-link]: https://img.shields.io/pypi/v/pytest-blender?logo=pypi\u0026logoColor=white\n[pypi-pyversions-badge-link]: https://img.shields.io/pypi/pyversions/pytest-blender?logo=python\u0026logoColor=white\n[license-image]: https://img.shields.io/pypi/l/pytest-blender?color=light-green\u0026logo=freebsd\u0026logoColor=white\n[license-link]: https://github.com/mondeja/pytest-blender/blob/master/LICENSE\n[tests-image]: https://img.shields.io/github/actions/workflow/status/mondeja/pytest-blender/ci.yml?branch=master\u0026logo=github\u0026label=tests\n[tests-link]: https://github.com/mondeja/pytest-blender/actions?query=workflow%3ACI\n[blender-downloader]: https://github.com/mondeja/blender-downloader\n[v1.2.1]: https://github.com/mondeja/pytest-blender/releases/tag/v1.2.1\n[Blender Directory Layout]: https://docs.blender.org/manual/en/latest/advanced/blender_directory_layout.html\n[`bpy.ops.preferences.addon_install`]: https://docs.blender.org/api/current/bpy.ops.preferences.html#bpy.ops.preferences.addon_install\n[`bpy.ops.wm.save_userpref`]: https://docs.blender.org/api/current/bpy.ops.wm.html#bpy.ops.wm.save_userpref\n[pytest-configuration]: https://docs.pytest.org/en/latest/reference/customize.html?highlight=configuration\n[pytest-cov]: https://github.com/pytest-dev/pytest-cov\n[pytest's `pythonpath` option]: https://docs.pytest.org/en/latest/reference/reference.html#confval-pythonpath\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmondeja%2Fpytest-blender","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmondeja%2Fpytest-blender","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmondeja%2Fpytest-blender/lists"}