Search code examples
pythonwindowsxlwings

xlwings keep app open beyond lifetime of pytest script


I'd like to test an Excel file using xlwings and pytest. While writing tests, it would be helpful to have an option to keep the xlwings.App instance open with the test file, so that I can inspect what's going on.

I'm using something like this:

import pytest
import xlwings as xw

EXCEL_VISIBLE = True  # toggle headless excel
EXCEL_KEEPALIVE = True  # toggle automatic closing of excel application
XL_TESTFILE = "myfile.xlsm"

@pytest.fixture(scope="module")
def file(request):
    # copy to temp directory, set up paths etc.

    with xw.App(visible=EXCEL_VISIBLE) as xl_app:
        xl_app.books.open(XL_TESTFILE)
        xl_app.activate(steal_focus=True)
        xl_app.macro('LoadData')()
        yield xl_app

    # FIXME: do something to optionally keep xl_app open, otherwise quit and unlink the test file
    if EXCEL_KEEPALIVE:
        pass
    else:
        pass

def simple_test(file):
    assert xl_app.books[XL_TESTFILE].sheets["mysheet"].range("A1").value == "expected"

How can I keep the application open after the tests complete, so that I can inspect what's happening. In the docs I can see the kill() and quit() methods, but no way to detach from the test script. Is that possible?

I don't care if keeping the app open results in manual cleanup - this is just something that makes it easier for me to write the tests.


Solution

  • The usual way to use a fixture is like this:

    @pytest.fixture(scope="module")
    def my_resource():
        resource = create_resource()
        yield resource
        destroy_resource()
    

    As you can see, you have no destroy_resource section and therefore expect your app to remain open.

    However, you have chosen to additionally use a context manager. I can only assume that the xlwings.App class has a __exit__ method defined which destroys the window. That method is called when the "with" statement finishes. So essentially, your app gets destroyed when the with_statement finishes, not when your test method finishes.

    To achieve the result you want, something like this should to the trick (code is untested):

    import pytest
    import xlwings as xw
    
    EXCEL_VISIBLE = True  # toggle headless excel
    EXCEL_KEEPALIVE = True  # toggle automatic closing of excel application
    XL_TESTFILE = "myfile.xlsm"
    
    @pytest.fixture(scope="module")
    def file(request):
        # copy to temp directory, set up paths etc.
    
        xl_app = xw.App(visible=EXCEL_VISIBLE)
        xl_app.books.open(XL_TESTFILE)
        xl_app.activate(steal_focus=True)
        xl_app.macro('LoadData')()
        yield xl_app
        if EXCEL_KEEPALIVE:
            pass
        else:
            xl_app.__exit__()