Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use pytest with bazel?

I have a my_module.py file that implements my_module and a file test_my_module.py that does import my_module and runs some tests written with pytest on it.

Normally I run the tests by cding into the directory that contains these two files and then doing

pytest

Now I want to use Bazel. I've added my_module.py as a py_binary but I don't know what the right way to invoke my tests is.

like image 429
Boris Avatar asked Oct 10 '19 15:10

Boris


People also ask

How do you test with Bazel?

Using a test target The most straightforward way to validate an artifact is to write a script and add a *_test target to your BUILD file. The specific artifacts you want to check should be data dependencies of this target.

Does Bazel work with Python?

As we expected, Bazel is using Python version 3.8. 3 that we compiled from scratch and not Python 3.9.

How does Bazel build work?

Bazel build process When running a build or a test, Bazel does the following: Loads the BUILD files relevant to the target. Analyzes the inputs and their dependencies, applies the specified build rules, and produces an action graph. Executes the build actions on the inputs until the final build outputs are produced.


2 Answers

Add the following code to test_my_module.py and mark the test script as a py_test instead of py_binary in your BUILD file:

if __name__ == "__main__":
    import pytest
    raise SystemExit(pytest.main([__file__]))

Then you can run your tests with bazel test test_my_module

like image 61
Eric Stdlib Avatar answered Oct 29 '22 13:10

Eric Stdlib


If you want to create a reusable code, that don't need add call to pytest add end of every python file with test. You can create py_test call that call a python file wrapping a call to pytest and keeping all argument. Then create a macro around the py_test. I explain the detailed solution in Experimentations on Bazel: Python (3), linter & pytest, with link to source code.

Create the python tool (wrapp call to pytest, or only pylint) in tools/pytest/pytest_wrapper.py

import sys
import pytest

# if using 'bazel test ...'
if __name__ == "__main__":
    sys.exit(pytest.main(sys.argv[1:]))


Create the macro in tools/pytest/defs.bzl

"""Wrap pytest"""

load("@rules_python//python:defs.bzl", "py_test")
load("@my_python_deps//:requirements.bzl", "requirement")

def pytest_test(name, srcs, deps = [], args = [], data = [], **kwargs):
    """
        Call pytest
    """
    py_test(
        name = name,
        srcs = [
            "//tools/pytest:pytest_wrapper.py",
        ] + srcs,
        main = "//tools/pytest:pytest_wrapper.py",
        args = [
            "--capture=no",
            "--black",
            "--pylint",
            "--pylint-rcfile=$(location //tools/pytest:.pylintrc)",
            # "--mypy",
        ] + args + ["$(location :%s)" % x for x in srcs],
        python_version = "PY3",
        srcs_version = "PY3",
        deps = deps + [
            requirement("pytest"),
            requirement("pytest-black"),
            requirement("pytest-pylint"),
            # requirement("pytest-mypy"),
        ],
        data = [
            "//tools/pytest:.pylintrc",
        ] + data,
        **kwargs
    )

expose some resources from tools/pytest/BUILD.bazel

exports_files([
    "pytest_wrapper.py",
    ".pylintrc",
])


Call it from your package BUILD.bazel

load("//tools/pytest:defs.bzl", "pytest_test")
...

pytest_test(
    name = "test",
    srcs = glob(["*.py"]),
    deps = [
        ...
    ],
)

then call bazel test //... pylint, pytest, back,... are part of the test flow

like image 26
David Bernard Avatar answered Oct 29 '22 13:10

David Bernard