{"id":5346,"library":"nutter","title":"Nutter: Databricks Notebook Testing Library","description":"Nutter is a Python library developed by Microsoft for robust unit and integration testing of Databricks notebooks. It streamlines the testing workflow for data and machine learning engineers by providing a framework to define test fixtures within notebooks. Nutter comprises two main components: the Nutter Runner (server-side, installed on Databricks clusters) and the Nutter CLI (client-side for local or CI/CD execution). It integrates easily with CI/CD pipelines like Azure DevOps, facilitating automated testing and quality assurance for Databricks-based data pipelines. The current version is 0.1.35, with a regular release cadence addressing enhancements and bug fixes.","status":"active","version":"0.1.35","language":"en","source_language":"en","source_url":"https://github.com/microsoft/nutter","tags":["databricks","testing","notebooks","ci-cd","etl","ml","azure"],"install":[{"cmd":"pip install nutter","lang":"bash","label":"Standard installation"},{"cmd":"%pip install nutter","lang":"python","label":"Installation in Databricks notebook"}],"dependencies":[],"imports":[{"note":"The NutterFixture is the base class for creating test fixtures in Databricks notebooks. The `tag` decorator is also commonly imported from the same module.","symbol":"NutterFixture","correct":"from runtime.nutterfixture import NutterFixture"}],"quickstart":{"code":"# Save this as a Databricks notebook, e.g., 'test_my_notebook'\n\n%pip install nutter\n\nfrom runtime.nutterfixture import NutterFixture\nimport os\n\nclass MyNotebookTestFixture(NutterFixture):\n    def __init__(self):\n        super().__init__()\n        # Initialize any test-specific variables or parameters\n        self.expected_value = 42\n\n    def before_all(self):\n        # This method runs once before all assertion methods.\n        # Typically, you would run the notebook under test here.\n        # For simplicity, we'll simulate a result.\n        # Example: dbutils.notebook.run('path/to/notebook_under_test', 600, {'param1': 'value1'})\n        self.actual_result = self.expected_value # Simulate successful notebook execution\n\n    def assertion_check_result_matches_expected(self):\n        # Nutter discovers methods prefixed with 'assertion_' as test cases.\n        assert self.actual_result == self.expected_value, \"The result should match the expected value\"\n\n    def assertion_ensure_truthy_condition(self):\n        # Another example test case\n        assert True, \"This condition should always be true\"\n\n    def after_all(self):\n        # This method runs once after all assertion methods have completed.\n        # Use it for cleanup, if necessary.\n        print(\"All tests completed for MyNotebookTestFixture.\")\n\n# Instantiate and execute the test fixture\nresult = MyNotebookTestFixture().execute_tests()\nprint(result.to_string())\n\n# Optional: Exit with a non-zero status in a Databricks job if tests fail\n# This is crucial for CI/CD pipelines to correctly report failures.\n# In a Databricks job, the environment variable 'DATABRICKS_IS_JOB' might be set,\n# or you can infer it from dbutils.notebook.entry_point.getDbutils().notebook().getContext().currentRunId().isDefined()\n# For local testing, this print acts as an indicator.\n# In a real Databricks job, you'd use dbutils.notebook.exit() if result.is_success is False.\nif os.environ.get('DATABRICKS_IS_JOB_RUN', 'False').lower() == 'true' and not result.is_success:\n    print(\"Tests failed in job context. Exiting with failure code.\")\n    # Example for actual job exit (requires dbutils):\n    # dbutils.notebook.exit(\"Tests failed\")\n","lang":"python","description":"This quickstart demonstrates how to create a Nutter test fixture within a Databricks notebook. It defines a class inheriting from `NutterFixture`, sets up `before_all` and `after_all` methods, and includes `assertion_` prefixed methods for individual test cases. The tests are executed by calling `execute_tests()` on an instance of the fixture."},"warnings":[{"fix":"Rename test methods from `run_testname` to `assertion_testname`. The `run_` method is still useful if you need to explicitly call `dbutils.notebook.run` within a specific test case.","message":"As of v0.1.33, the `run_` prefix is no longer required for defining test cases. Instead, you define multiple `assertion_` methods, and they are executed after the `before_all` method. Code relying solely on `run_` methods for test discovery will need to adapt to the `assertion_` convention.","severity":"breaking","affected_versions":">=0.1.33"},{"fix":"Set `DATABRICKS_HOST` and `DATABRICKS_TOKEN` environment variables in your execution environment (e.g., shell, CI/CD pipeline).","message":"Nutter CLI execution (outside Databricks notebooks) requires specific environment variables to authenticate with Databricks: `DATABRICKS_HOST` (the workspace URL) and `DATABRICKS_TOKEN` (a personal access token). Failure to set these will prevent CLI execution.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure your test notebooks are named `test_your_notebook_name.py` (or `.ipynb`). When using the CLI to run multiple tests via pattern, omit the `test_` prefix in the pattern itself.","message":"Test notebooks must follow a naming convention, typically `test_<notebook_under_test>`. The Nutter CLI and Runner components rely on this pattern for automatic test discovery.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Consider using alternative approaches for capturing and reporting test results (e.g., JUnit format) rather than relying solely on standard output. Investigation into daemon threads in the notebook's code or test environment might also be necessary.","message":"When Nutter tests are run in Azure DevOps pipelines, users have reported `Fatal Python error: _enter_buffered_busy: could not acquire lock for <_io.BufferedWriter name='<stdout>'>` errors during interpreter shutdown. This can obscure actual test results.","severity":"gotcha","affected_versions":"Reported with v0.1.35, potentially earlier versions."},{"fix":"If faster status updates are required for specific scenarios, you can adjust the polling interval using the `poll_wait_time` flag in the Nutter CLI.","message":"The default polling interval for checking notebook execution status increased from 1 second to 5 seconds in v0.1.34. This means tests might take longer to report completion, though it can be controlled.","severity":"gotcha","affected_versions":">=0.1.34"}],"env_vars":null,"last_verified":"2026-04-13T00:00:00.000Z","next_check":"2026-07-12T00:00:00.000Z"}