nbval icon indicating copy to clipboard operation
nbval copied to clipboard

Is it possible to mock an object globally for all notebooks, eg with conftest.py?

Open ca-scribner opened this issue 4 years ago • 1 comments

I have a suite of example notebooks that I wish to use as tests. Many use a python package to connect to a server, but when testing the syntax for making that connection is different. For example, in general the notebooks invoke Client() and because they're in the prod environment the connection requires no additional information, but the tests are run from a different runner which needs something like Client(additional_credentials).

Client is from an external library I cannot change and does not provide a good way to override the defaults from environment variables, etc. My plan was to mock the Client() during testing by using a session-scoped, autouse=True fixture in a conftest.py to inject the extra information into Client() calls. This works for regular pytests (.py files), but the notebooks evaluated by nbval do not have their Client() mocked over.

Schematic example:

client.py

class Client():
  def __init__(self, creds):
    self.x = creds

conftest.py

class ModifedClient():
  def __init__(self, *args, **kwargs):
    self.x = "some other creds"

@pytest.fixture(scope="session", autouse=True)
def patch_over_client(request):
  patched = mock.patch('client.Client', side_effect=ModifiedClient)
  active_mock = patched.start()
  def unpatch():
    patched.stop()
  request.addfinalizer(unpatch)

calling_notebook.ipynb

# (pretend these are cells)
from client import Client
c = Client()
assert c.x == "some other creds"

Is there a way to make this work or a better pattern achieve the same result?

ca-scribner avatar Jan 07 '21 17:01 ca-scribner

I don't think there's any great way to do this, because the notebook code is running in a separate process - nbval launches a Jupyter kernel, and sends the cells to that. It might be possible to hack something with .pth files or a sitecustomize.py (see the site module docs) to make code run when the kernel is starting up. But that definitely wouldn't be elegant, and I don't know quite how to ensure it's cleaned up at the right time.

takluyver avatar Jan 13 '21 14:01 takluyver