Testing with Pytest

xlwings Lite has a built-in pytest runner, allowing you to write automated tests for your Python code as well as for the workbook itself.

Create one or more test files, then click the green Run all tests button to run them all, or pick a single test file from the dropdown to run just that file’s tests. You can also click the green play button in the gutter next to any test function to run that one test.

Note

pytest isn’t installed by default. Add pytest to requirements.txt, followed by a restart.

Test files and functions

A test file is any file whose name matches pytest’s convention: test_*.py (e.g. test_main.py) or *_test.py (e.g. sheet_test.py). Create them with New File… in the Source files menu.

A test function is a function whose name starts with test_. Use assert statements to check conditions:

def test_addition():
    assert 1 + 1 == 2

Tests can also be grouped in a class, following pytest’s convention of a Test-prefixed name. Both sync and async def methods are supported, and each gets a play button in the gutter just like top-level tests:

class TestMath:
    def test_add(self):
        assert 1 + 1 == 2

    async def test_subtract(self):
        assert 3 - 1 == 2

Importing your own code

Tests can import functions from other modules such as main.py:

# main.py
from xlwings import func


@func
def hello(name: str):
    return f"Hello {name}!"
# test_main.py
from main import hello


def test_hello():
    assert hello("xlwings") == "Hello xlwings!"

Async tests

Tests defined as async def are supported. However, they’re executed outside of pytest, so pytest fixtures, markers, and plugins aren’t supported. For an async class method, the class is instantiated per test, but setup_method and other pytest hooks don’t run.

import xlwings as xw


async def test_values():
    book = await xw.books.get_active()  # See docs about Async API
    values = await book.sheets[0]["A1:B2"].get_value()
    assert values == [[1, 2], [3, 4]]

Accessing the active workbook

Both the sync API (xw.books.active) and async API (await xw.books.get_active()) work in tests. The sync API fetches values for every sheet — for large workbooks, prefer await xw.books.get_active() from an async test, which loads on demand, see previous example and Async API.

Configuring pytest

Add a [tool.pytest] section to pyproject.toml. For example:

[tool.pytest]
addopts = "-s"  # show print() output from tests

Restart xlwings Lite after changing pyproject.toml.

Note

  • Async tests don’t go through pytest, so options under [tool.pytest] only affect sync tests.

  • [tool.pytest] is only supported with pytest 9.0+. Pyodide 0.27.5 comes with an older version of pytest, so use [tool.pytest.ini_options] instead (you can change your Pyodide version under xlwings Lite menu > Settings > Workbook).

Limitations

  • Async pytest plugins like pytest-asyncio are not supported — xlwings Lite runs async tests outside of pytest instead.

  • conftest.py is supported for sync tests. Its fixtures and hooks don’t apply to async tests, since those run outside of pytest.