metaprogramming and politics

Decentralize. Take the red pill.

Putting test-hooks into local and global plugins

with one comment

(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 file in your test directory or package root directory with these contents:

def pytest_generate_tests(metafunc):
     # exactly same implementation as module-level one

As with test files and functions and arguments, 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 file:


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

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.

  1. Thanks for the blog ! Could you please explain in which order hooks executed and
    **********what it does in each section.*************
    └── 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

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: