Putting test-hooks into local and global plugins
(updated to match 1.0 API and features) I’d like to clarify py.test’s hook lookup and showcase the ease of writing things into per-project or global plugins. The pytest_generate_tests was discussed in the last blog post on parametrizing tests. Here is where you can write this hook down:
- in the test module
- in a local plugin
- or in a global plugin
The last blog post showed how to put the hook directly into the test module. Let’s take a look now what putting a hook into a "local or global plugin" means.
Putting a Hook in a local plugin
Putting the generate-hook into a local plugin means to create a conftest.py file in your test directory or package root directory with these contents:
#./conftest.py def pytest_generate_tests(metafunc): # exactly same implementation as module-level one
As with test files and functions and arguments, conftest.py files and the exact name ConftestPlugin will be automatically discovered and the plugin be instantiated.
Put hook into a global plugin
Putting the generate-hook in a global cross-project plugin requires to invent a file or package name with a fixed pytest_ prefix. Here is how you would write down the generate hook into a self-contained pytest_mygen.py file:
#./pytest_mygen.py def pytest_generate_tests(metafunc): # exactly same implementation as module-level one
The hook name including its metafunc argument needs to be used exactly as decribed – loading your plugin will otherwise result in an error.
Activating a global plugin
While local plugins are automatically discovered, global plugins need to specified. To activate a global plugin pytest_mygen you can use either of the following three ways:
py.test -p mygen # for command line activation export PYTEST_PLUGINS=mygen # for shell/env activation pytest_plugins = "mygen" # in a test module or conftest.py
py.test loads command-line or environment-specified plugins very early so that plugins can add command line options.
multiple pytest_generate_hook implementations
All existing pytests_generate_hooks hooks will be called once for each test function. You can have multiple hooks but the generate-hook usually only acts on a specific funcarg by doing a check like this:
if "myarg" in metafunc.funcargnames: ...
So, you say, what about a test function with multiple arguments – could each function argument come from a different generating provider factory?
This would mean that multiple generators act independently but want to collaborate and combine their values for a given test functions. Well, if you encounter a real need for it, please come forward and we’ll think up a fitting API extension. A couple of days ago i had a "combining funcargs" API call implemented but decided to remove it because i try hard these days to only add features that have a proven need.
Putting test support code into plugins FTW
Lastly, let me point out that putting the pytest_generate_tests hook into a plugin allows the actual test code to stay igorant about the exact way how or where parametrization is implemented. For example, adding command line options for influencing the generation or selection of parameter sets, including randomizing, would not change a single character in the test module and test code.
have fun and let me know what you think, holger
Written by holger krekel
May 14, 2009 at 12:01 pm
Posted in metaprogramming
One Response
Subscribe to comments with RSS.
Thanks for the blog ! Could you please explain in which order hooks executed and
**********what it does in each section.*************
root
└── pytest_cmdline_main
├── pytest_plugin_registered
├── pytest_configure
│ └── pytest_plugin_registered
├── pytest_sessionstart
│ ├── pytest_plugin_registered
│ └── pytest_report_header
├── pytest_collection
│ ├── pytest_collectstart
│ ├── pytest_make_collect_report
│ │ ├── pytest_collect_file
│ │ │ └── pytest_pycollect_makemodule
│ │ └── pytest_pycollect_makeitem
│ │ └── pytest_generate_tests
│ │ └── pytest_make_parametrize_id
│ ├── pytest_collectreport
│ ├── pytest_itemcollected
│ ├── pytest_collection_modifyitems
│ └── pytest_collection_finish
│ └── pytest_report_collectionfinish
├── pytest_runtestloop
│ └── pytest_runtest_protocol
│ ├── pytest_runtest_logstart
│ ├── pytest_runtest_setup
│ │ └── pytest_fixture_setup
│ ├── pytest_runtest_makereport
│ ├── pytest_runtest_logreport
│ │ └── pytest_report_teststatus
│ ├── pytest_runtest_call
│ │ └── pytest_pyfunc_call
│ ├── pytest_runtest_teardown
│ │ └── pytest_fixture_post_finalizer
│ └── pytest_runtest_logfinish
├── pytest_sessionfinish
│ └── pytest_terminal_summary
└── pytest_unconfigure
Mukunthan Ragavan
January 6, 2021 at 4:24 pm