pytest skippy¶
Automatically skip tests that don’t need to run!
This library works with pytest to generate a complete import graph for your tests. If nothing in the import graph has changed (according to GIT), the test is skipped.
Quickstart¶
Requirements¶
This package interacts with GIT and assumes that your code and tests both exist within the same git repository.
- GIT
- pytest>=2.3.4
Installation¶
pip install pytest-skippy
Usage¶
This command uses the default target branch of origin/master
. The target
branch is the branch that has been modified. Most users should not have to
change the target branch when getting started.
py.test --skippy
User Guide¶
Overview¶
pytest-skippy automatically skips tests that don’t need to run!
The process for determining if a test needs to run involves
PyTest Command Line Options¶
--skippy-target-branch
¶
(Default: origin/master
)
The target branch is the branch that has been modified. This branch parameter
is passed to git merge-base
to determine which files have been changed.
For example, for a pull request using github and Travis-CI, the target branch
is stored in an environment variable called TRAVIS_PULL_REQUEST_BRANCH
.
--skippy-safe
¶
(Default: False
)
Safe mode changes the behavior of the skippy plugin so that any import that cannot be resolved forces a test run.
This makes sure that if a module is missing for any reason the test that would catch the missing import is run.
Using this mode is likely to result in many false positives, causing tests to run when it may not be necessary.
Limitations¶
Since this library does not actually import any code (due to potential side effects of importing and performance), there are a number of limitations surrounding the accuracy of this plugin.
Runtime changes to sys.path
¶
This library does not account for any changes to the python path at runtime. Modifications to the python path at runtime may change which file is loaded by an import statement.
If your code modifies sys.path
at runtime, the use of --skippy-safe is
recommended.
Importing modules with from
statements¶
The type of literals that are imported with from
style imports is ambiguous.
Consider the following statement:
from foo import bar
In the statement above, is bar
an attribute of foo
or a module?
It’s possible that foo.bar
is a module and the type of bar
will be a
module. In these cases, import foo.bar
will succeed at runtime.
However, if bar
is a function, import foo.bar
will fail at runtime.
This plugin does not attempt to determine the type of bar
.
By default, pytest-skippy assumes that if foo.bar
cannot be located,
bar
must be an attribute of the module foo
. As a result, if bar
is
in fact a missing module, the test will be skipped by default.
Therefore, if module imports with the from
statement are expected,
--skippy-safe should be used.
pytest_skippy¶
pytest_skippy.core¶
pytest_skippy.git¶
-
pytest_skippy.git.
detect_changed_files
(target_branch, base_branch='HEAD', git_repo_dir=None)¶ Get a list of changed files in a git repo
Parameters: Returns: A set of files that have changed in the git repository.
Return type:
pytest_skippy.imp¶
-
pytest_skippy.imp.
convert_module_to_filename
(module_name)¶ Find a module’s file location
Returns the canonical path to the file that defines a module. The canonical path is retrieved using a call to
os.path.realpath()
Parameters: module_name (str) – Full path to a module. Returns: A string containing the filename of the requested module. Return type: str Example:
>>> import os.path >>> path = convert_module_to_filename('re') >>> os.path.split(path)[-1] 're.py'
pytest_skippy.parse¶
-
pytest_skippy.parse.
get_imported_modules
(filename)¶ Return modules that are imported by a file
This function will extract all modules that are imported within a python file. The return value includes imports that happen at scopes other than the top level scope.
This parser does not execute any of the python code in the file.
The parser will also return a set of confirmed modules (strings that are guaranteed to be interpreted as module types at runtime)
from foo import bar # foo is a confirmed module, bar is a candidate import bar # bar is a confirmed module
For example:
>>> import tempfile >>> with tempfile.NamedTemporaryFile(delete=False) as f: ... x = f.write(b''' ... import os ... ... def inner(): ... import re ... ''') >>> modules, _ = get_imported_modules(f.name) >>> print(sorted([m for m in modules])) ['os', 're'] >>> import os ; os.unlink(f.name)
Parameters: filename (str) – File path to a python file Returns: (modules, confirmed_modules) Return type: tuple
pytest_skippy.util¶
-
pytest_skippy.util.
flatten_imports
(imported_module, import_tree)¶ Returns a set of all modules imported by imported_module
The import tree is a
dict
in the form {module: set(imported_by)}Parameters: Returns: A set of modules that import imported_module
Return type: Example: A is imported by B
>>> flat = flatten_imports('A', {'A': {'B'}, 'B': set()}) >>> sorted(list(flat)) ['A', 'B']