<h1 align="center">pytest-plone</h1>
<div align="center">
[![PyPI](https://img.shields.io/pypi/v/pytest-plone)](https://pypi.org/project/pytest-plone/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pytest-plone)](https://pypi.org/project/pytest-plone/)
[![PyPI - Wheel](https://img.shields.io/pypi/wheel/pytest-plone)](https://pypi.org/project/pytest-plone/)
[![PyPI - License](https://img.shields.io/pypi/l/pytest-plone)](https://pypi.org/project/pytest-plone/)
[![PyPI - Status](https://img.shields.io/pypi/status/pytest-plone)](https://pypi.org/project/pytest-plone/)
[![PyPI - Plone Versions](https://img.shields.io/pypi/frameworkversions/plone/pytest-plone)](https://pypi.org/project/pytest-plone/)
[![Code analysis checks](https://github.com/collective/pytest-plone/actions/workflows/code-analysis.yml/badge.svg)](https://github.com/collective/pytest-plone/actions/workflows/code-analysis.yml)
[![Tests](https://github.com/collective/pytest-plone/actions/workflows/tests.yml/badge.svg)](https://github.com/collective/pytest-plone/actions/workflows/tests.yml)
![Code Style](https://img.shields.io/badge/Code%20Style-Black-000000)
[![GitHub contributors](https://img.shields.io/github/contributors/collective/pytest-plone)](https://github.com/collective/pytest-plone)
[![GitHub Repo stars](https://img.shields.io/github/stars/collective/pytest-plone?style=social)](https://github.com/collective/pytest-plone)
</div>
**pytest-plone** is a [pytest](https://docs.pytest.org) plugin providing fixtures and helpers to test [Plone](https://plone.org) add-ons.
This package is built on top of [gocept.pytestlayer](https://github.com/gocept/gocept.pytestlayer).
## Reasoning
Despite the fact Plone, and Zope, have their codebases tested with `unittest`, over the years
`pytest` became the most popular choice for testing in Python.
`pytest` is more flexible and easier to use than `unittest` and has a rich ecosystem of plugins that you can use to extend its functionality.
## Warning
**THIS PACKAGE IS NOT CONSIDERED TO BE STABLE AND FIXTURES COULD CHANGE BEFORE A FINAL RELEASE**
## Usage
In your top-level `conftest.py` import your testing layers, and also import `fixtures_factory` -- which will accept a iterator of tuples containing the testing layer and a prefix to be used to generate the needed pytest fixtures.
```python
from Products.CMFPlone.testing import PRODUCTS_CMFPLONE_FUNCTIONAL_TESTING
from Products.CMFPlone.testing import PRODUCTS_CMFPLONE_INTEGRATION_TESTING
from pytest_plone import fixtures_factory
pytest_plugins = ["pytest_plone"]
globals().update(
fixtures_factory(
(
(PRODUCTS_CMFPLONE_FUNCTIONAL_TESTING, "functional"),
(PRODUCTS_CMFPLONE_INTEGRATION_TESTING, "integration"),
)
)
)
```
In the code above, the following pytest fixtures will be available to your tests:
| Fixture | Scope |
| --- | --- |
| functional_session | Session |
| functional_class | Class |
| functional | Function |
| integration_session | Session |
| integration_class | Class |
| integration | Function |
## Fixtures
### app
| | |
| --- | --- |
| Description | Zope root |
| Required Fixture | **integration** |
| Scope | **Function** |
```python
def test_app(app):
"""Test portal title."""
assert app.getPhysicalPath() == ("", )
```
### portal
| | |
| --- | --- |
| Description | Portal object |
| Required Fixture | **integration** |
| Scope | **Function** |
```python
def test_portal_title(portal):
"""Test portal title."""
assert portal.title == "Plone Site"
```
### http_request
| | |
| --- | --- |
| Description | HTTP Request |
| Required Fixture | **integration** |
| Scope | **Function** |
```python
from plone import api
def test_myproduct_controlpanel_view(portal, http_request):
"""Test myproduct_controlpanel browser view is available."""
view = api.content.get_view(
"myproduct-controlpanel", portal, http_request
)
assert view is not None
```
### installer
| | |
| --- | --- |
| Description | Installer browser view. Used to install/uninstall/check for an add-on. |
| Required Fixture | **integration** |
| Scope | **Function** |
```python
import pytest
PACKAGE_NAME = "myproduct"
@pytest.fixture
def uninstall(installer):
"""Fixture to uninstall a package."""
installer.uninstall_product(PACKAGE_NAME)
def test_product_installed(installer):
"""Test if myproduct is installed."""
assert installer.is_product_installed(PACKAGE_NAME) is True
@pytest.mark.parametrize(
"package",
[
"collective.casestudy",
"pytest-plone",
]
)
def test_dependency_installed(installer, package):
"""Test if dependency is installed."""
assert installer.is_product_installed(package) is True
```
### browser_layers
| | |
| --- | --- |
| Description | List of available browser layers. Used to test if a specific browser layer is registered. |
| Required Fixture | **integration** |
| Scope | **Function** |
```python
def test_browserlayer(browser_layers):
"""Test that IMyProductLayer is registered."""
from myproduct.interfaces import IMyProductLayer
assert IMyProductLayer in browser_layers
```
### controlpanel_actions
| | |
| --- | --- |
| Description | List of control panel actions ids. Used to test if a specific control panel is installed or not. |
| Required Fixture | **integration** |
| Scope | **Function** |
```python
def test_configlet_install(controlpanel_actions):
"""Test if control panel is installed."""
assert "myproductcontrolpanel" in controlpanel_actions
```
### get_fti
| | |
| --- | --- |
| Description | Function to get the Factory Type Info (FTI) for a content type. |
| Required Fixture | **integration** |
| Scope | **Function** |
```python
def test_get_fti(get_fti):
"""Test if Document fti is installed."""
assert get_fti("Document") is not None
```
### get_behaviors
| | |
| --- | --- |
| Description | Function to list behaviors for a content type. |
| Required Fixture | **integration** |
| Scope | **Function** |
```python
import pytest
def test_block_in_document(get_behaviors):
"""Test if blocks behavior is installed for Document."""
assert "volto.blocks" in get_behaviors("Document")
@pytest.mark.parametrize(
"behavior",
[
"plone.dublincore",
"plone.namefromtitle",
"plone.shortname",
"plone.excludefromnavigation",
"plone.relateditems",
"plone.versioning",
"volto.blocks",
"volto.navtitle",
"volto.preview_image",
"volto.head_title",
],
)
def test_has_behavior(self, get_behaviors, behavior):
assert behavior in get_behaviors("Document")
```
### get_vocabulary
| | |
| --- | --- |
| Description | Function to get a named vocabulary. |
| Required Fixture | **integration** |
| Scope | **Function** |
```python
from zope.schema.vocabulary import SimpleVocabulary
VOCAB = "plone.app.vocabularies.AvailableContentLanguages"
def test_get_vocabulary(get_vocabulary):
"""Test plone.app.vocabularies.AvailableContentLanguages."""
vocab = get_vocabulary(VOCAB)
assert vocab is not None
assert isinstance(vocab, SimpleVocabulary)
```
### setup_tool
| | |
| --- | --- |
| Description | Portal Setup tool. |
| Required Fixture | **integration** |
| Scope | **Function** |
```python
def test_setup_tool(setup_tool):
"""Test setup_tool."""
assert setup_tool is not None
```
### profile_last_version
| | |
| --- | --- |
| Description | Function to get the last version of a profile. |
| Required Fixture | **integration** |
| Scope | **Function** |
```python
PACKAGE_NAME = "collective.case_study"
def test_last_version(profile_last_version):
"""Test setup_tool."""
profile = f"{PACKAGE_NAME}:default"
version = profile_last_version(profile)
assert version == "1000"
```
## Plugin Development
You need a working `python` environment (system, virtualenv, pyenv, etc) version 3.8 or superior.
Then install the dependencies and a development instance using:
```bash
make build
```
To run tests for this package:
```bash
make test
```
By default we use the latest Plone version in the 6.x series.
## License
The project is licensed under the GPLv2.
# Changelog
## 0.2.0 (2023-01-05)
- Add `app` fixture.
[ericof]
- Add `setup_tool` and `profile_last_version` fixtures.
[ericof]
- Add `get_fti` and `get_behaviors` fixtures.
[ericof]
- Add `get_vocabulary` fixture.
[ericof]
## 0.1.0 (2023-01-04)
- Fixtures `portal`, `http_request`, `installer`, `browser_layers`, `controlpanel_actions`
[ericof]
- Initial release
[ericof]
Raw data
{
"_id": null,
"home_page": "http://pypi.python.org/pypi/pytest-plone",
"name": "pytest-plone",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "",
"keywords": "Plone Pytest Testing",
"author": "Plone",
"author_email": "test@plone.org",
"download_url": "https://files.pythonhosted.org/packages/df/c9/628915a852df6d806d1052fb312af1beac207792331593c6731978649289/pytest-plone-0.2.0.tar.gz",
"platform": null,
"description": "\n<h1 align=\"center\">pytest-plone</h1>\n\n<div align=\"center\">\n\n[![PyPI](https://img.shields.io/pypi/v/pytest-plone)](https://pypi.org/project/pytest-plone/)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pytest-plone)](https://pypi.org/project/pytest-plone/)\n[![PyPI - Wheel](https://img.shields.io/pypi/wheel/pytest-plone)](https://pypi.org/project/pytest-plone/)\n[![PyPI - License](https://img.shields.io/pypi/l/pytest-plone)](https://pypi.org/project/pytest-plone/)\n[![PyPI - Status](https://img.shields.io/pypi/status/pytest-plone)](https://pypi.org/project/pytest-plone/)\n\n\n[![PyPI - Plone Versions](https://img.shields.io/pypi/frameworkversions/plone/pytest-plone)](https://pypi.org/project/pytest-plone/)\n\n[![Code analysis checks](https://github.com/collective/pytest-plone/actions/workflows/code-analysis.yml/badge.svg)](https://github.com/collective/pytest-plone/actions/workflows/code-analysis.yml)\n[![Tests](https://github.com/collective/pytest-plone/actions/workflows/tests.yml/badge.svg)](https://github.com/collective/pytest-plone/actions/workflows/tests.yml)\n![Code Style](https://img.shields.io/badge/Code%20Style-Black-000000)\n\n[![GitHub contributors](https://img.shields.io/github/contributors/collective/pytest-plone)](https://github.com/collective/pytest-plone)\n[![GitHub Repo stars](https://img.shields.io/github/stars/collective/pytest-plone?style=social)](https://github.com/collective/pytest-plone)\n</div>\n\n**pytest-plone** is a [pytest](https://docs.pytest.org) plugin providing fixtures and helpers to test [Plone](https://plone.org) add-ons.\n\n\nThis package is built on top of [gocept.pytestlayer](https://github.com/gocept/gocept.pytestlayer).\n\n\n## Reasoning\n\nDespite the fact Plone, and Zope, have their codebases tested with `unittest`, over the years\n`pytest` became the most popular choice for testing in Python.\n\n`pytest` is more flexible and easier to use than `unittest` and has a rich ecosystem of plugins that you can use to extend its functionality.\n## Warning\n\n**THIS PACKAGE IS NOT CONSIDERED TO BE STABLE AND FIXTURES COULD CHANGE BEFORE A FINAL RELEASE**\n\n## Usage\n\nIn your top-level `conftest.py` import your testing layers, and also import `fixtures_factory` -- which will accept a iterator of tuples containing the testing layer and a prefix to be used to generate the needed pytest fixtures.\n\n```python\nfrom Products.CMFPlone.testing import PRODUCTS_CMFPLONE_FUNCTIONAL_TESTING\nfrom Products.CMFPlone.testing import PRODUCTS_CMFPLONE_INTEGRATION_TESTING\nfrom pytest_plone import fixtures_factory\n\n\npytest_plugins = [\"pytest_plone\"]\n\n\nglobals().update(\n fixtures_factory(\n (\n (PRODUCTS_CMFPLONE_FUNCTIONAL_TESTING, \"functional\"),\n (PRODUCTS_CMFPLONE_INTEGRATION_TESTING, \"integration\"),\n )\n )\n)\n```\n\nIn the code above, the following pytest fixtures will be available to your tests:\n\n| Fixture | Scope |\n| --- | --- |\n| functional_session | Session |\n| functional_class | Class |\n| functional | Function |\n| integration_session | Session |\n| integration_class | Class |\n| integration | Function |\n\n\n## Fixtures\n\n### app\n\n| | |\n| --- | --- |\n| Description | Zope root |\n| Required Fixture | **integration** |\n| Scope | **Function** |\n\n```python\ndef test_app(app):\n \"\"\"Test portal title.\"\"\"\n assert app.getPhysicalPath() == (\"\", )\n\n```\n\n### portal\n\n| | |\n| --- | --- |\n| Description | Portal object |\n| Required Fixture | **integration** |\n| Scope | **Function** |\n\n```python\ndef test_portal_title(portal):\n \"\"\"Test portal title.\"\"\"\n assert portal.title == \"Plone Site\"\n\n```\n\n### http_request\n\n| | |\n| --- | --- |\n| Description | HTTP Request |\n| Required Fixture | **integration** |\n| Scope | **Function** |\n\n\n```python\nfrom plone import api\n\n\ndef test_myproduct_controlpanel_view(portal, http_request):\n \"\"\"Test myproduct_controlpanel browser view is available.\"\"\"\n view = api.content.get_view(\n \"myproduct-controlpanel\", portal, http_request\n )\n assert view is not None\n\n```\n\n### installer\n\n| | |\n| --- | --- |\n| Description | Installer browser view. Used to install/uninstall/check for an add-on. |\n| Required Fixture | **integration** |\n| Scope | **Function** |\n\n\n```python\nimport pytest\n\n\nPACKAGE_NAME = \"myproduct\"\n\n\n@pytest.fixture\ndef uninstall(installer):\n \"\"\"Fixture to uninstall a package.\"\"\"\n installer.uninstall_product(PACKAGE_NAME)\n\n\ndef test_product_installed(installer):\n \"\"\"Test if myproduct is installed.\"\"\"\n assert installer.is_product_installed(PACKAGE_NAME) is True\n\n@pytest.mark.parametrize(\n \"package\",\n [\n \"collective.casestudy\",\n \"pytest-plone\",\n ]\n)\ndef test_dependency_installed(installer, package):\n \"\"\"Test if dependency is installed.\"\"\"\n assert installer.is_product_installed(package) is True\n\n```\n\n### browser_layers\n\n| | |\n| --- | --- |\n| Description | List of available browser layers. Used to test if a specific browser layer is registered. |\n| Required Fixture | **integration** |\n| Scope | **Function** |\n\n```python\ndef test_browserlayer(browser_layers):\n \"\"\"Test that IMyProductLayer is registered.\"\"\"\n from myproduct.interfaces import IMyProductLayer\n\n\n assert IMyProductLayer in browser_layers\n\n```\n\n### controlpanel_actions\n\n| | |\n| --- | --- |\n| Description | List of control panel actions ids. Used to test if a specific control panel is installed or not. |\n| Required Fixture | **integration** |\n| Scope | **Function** |\n\n```python\ndef test_configlet_install(controlpanel_actions):\n \"\"\"Test if control panel is installed.\"\"\"\n assert \"myproductcontrolpanel\" in controlpanel_actions\n\n```\n\n### get_fti\n\n| | |\n| --- | --- |\n| Description | Function to get the Factory Type Info (FTI) for a content type. |\n| Required Fixture | **integration** |\n| Scope | **Function** |\n\n```python\ndef test_get_fti(get_fti):\n \"\"\"Test if Document fti is installed.\"\"\"\n assert get_fti(\"Document\") is not None\n\n```\n\n### get_behaviors\n\n| | |\n| --- | --- |\n| Description | Function to list behaviors for a content type. |\n| Required Fixture | **integration** |\n| Scope | **Function** |\n\n```python\nimport pytest\n\n\ndef test_block_in_document(get_behaviors):\n \"\"\"Test if blocks behavior is installed for Document.\"\"\"\n assert \"volto.blocks\" in get_behaviors(\"Document\")\n\n\n@pytest.mark.parametrize(\n \"behavior\",\n [\n \"plone.dublincore\",\n \"plone.namefromtitle\",\n \"plone.shortname\",\n \"plone.excludefromnavigation\",\n \"plone.relateditems\",\n \"plone.versioning\",\n \"volto.blocks\",\n \"volto.navtitle\",\n \"volto.preview_image\",\n \"volto.head_title\",\n ],\n)\ndef test_has_behavior(self, get_behaviors, behavior):\n assert behavior in get_behaviors(\"Document\")\n```\n\n### get_vocabulary\n\n| | |\n| --- | --- |\n| Description | Function to get a named vocabulary. |\n| Required Fixture | **integration** |\n| Scope | **Function** |\n\n```python\nfrom zope.schema.vocabulary import SimpleVocabulary\n\nVOCAB = \"plone.app.vocabularies.AvailableContentLanguages\"\n\ndef test_get_vocabulary(get_vocabulary):\n \"\"\"Test plone.app.vocabularies.AvailableContentLanguages.\"\"\"\n vocab = get_vocabulary(VOCAB)\n assert vocab is not None\n assert isinstance(vocab, SimpleVocabulary)\n\n```\n\n### setup_tool\n\n| | |\n| --- | --- |\n| Description | Portal Setup tool. |\n| Required Fixture | **integration** |\n| Scope | **Function** |\n\n```python\ndef test_setup_tool(setup_tool):\n \"\"\"Test setup_tool.\"\"\"\n assert setup_tool is not None\n\n```\n\n### profile_last_version\n\n| | |\n| --- | --- |\n| Description | Function to get the last version of a profile. |\n| Required Fixture | **integration** |\n| Scope | **Function** |\n\n```python\nPACKAGE_NAME = \"collective.case_study\"\n\ndef test_last_version(profile_last_version):\n \"\"\"Test setup_tool.\"\"\"\n profile = f\"{PACKAGE_NAME}:default\"\n version = profile_last_version(profile)\n assert version == \"1000\"\n\n```\n## Plugin Development\n\nYou need a working `python` environment (system, virtualenv, pyenv, etc) version 3.8 or superior.\n\nThen install the dependencies and a development instance using:\n\n```bash\nmake build\n```\n\nTo run tests for this package:\n\n```bash\nmake test\n```\n\nBy default we use the latest Plone version in the 6.x series.\n\n## License\n\nThe project is licensed under the GPLv2.\n\n\n# Changelog\n\n## 0.2.0 (2023-01-05)\n\n\n- Add `app` fixture.\n [ericof]\n\n- Add `setup_tool` and `profile_last_version` fixtures.\n [ericof]\n\n- Add `get_fti` and `get_behaviors` fixtures.\n [ericof]\n\n- Add `get_vocabulary` fixture.\n [ericof]\n\n\n## 0.1.0 (2023-01-04)\n\n- Fixtures `portal`, `http_request`, `installer`, `browser_layers`, `controlpanel_actions`\n [ericof]\n\n- Initial release\n [ericof]\n\n\n",
"bugtrack_url": null,
"license": "GPL",
"summary": "Pytest plugin to test Plone addons",
"version": "0.2.0",
"split_keywords": [
"plone",
"pytest",
"testing"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "9caeb5510ea5f3feb50df6082f78eddf578cabaf03c14925e070acbbbd79ed2c",
"md5": "4df9a459b90e1baa6935daffa4bc1fbf",
"sha256": "9e0cbeeaadf4265e800e5cd6c5d3d1e429a5ed8acf9c3e819a57b695efb86be1"
},
"downloads": -1,
"filename": "pytest_plone-0.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "4df9a459b90e1baa6935daffa4bc1fbf",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 13802,
"upload_time": "2023-01-05T19:43:20",
"upload_time_iso_8601": "2023-01-05T19:43:20.555421Z",
"url": "https://files.pythonhosted.org/packages/9c/ae/b5510ea5f3feb50df6082f78eddf578cabaf03c14925e070acbbbd79ed2c/pytest_plone-0.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "dfc9628915a852df6d806d1052fb312af1beac207792331593c6731978649289",
"md5": "274c462d72f607bfbc30412a9255e286",
"sha256": "b61f45679627b56e60c7d8c28cef841463e6b1aa8e7c5bfbbc92ee2291f32e7a"
},
"downloads": -1,
"filename": "pytest-plone-0.2.0.tar.gz",
"has_sig": false,
"md5_digest": "274c462d72f607bfbc30412a9255e286",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 17942,
"upload_time": "2023-01-05T19:43:22",
"upload_time_iso_8601": "2023-01-05T19:43:22.243156Z",
"url": "https://files.pythonhosted.org/packages/df/c9/628915a852df6d806d1052fb312af1beac207792331593c6731978649289/pytest-plone-0.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-01-05 19:43:22",
"github": false,
"gitlab": false,
"bitbucket": false,
"lcname": "pytest-plone"
}