CertSafari
    Databricks Certified Machine Learning Professional· Lessons

    Domain 2 · Lesson 6/11

    Unit Testing Functions in Databricks Notebooks

    Validation Testing

    11 min read
    9.09% of exam
    5 sources
    Published 3 Oct 2026
    Docs as of 30 Sep 2026

    What you will be able to do

    • Design functions that return a single predictable outcome so they can be unit tested
    • Write and run unit tests in a Databricks notebook with unittest, widgets, and pytest naming conventions
    • Compare the three ways of organizing functions and their unit tests, and pick the one Databricks recommends for each language
    • Identify which kinds of testing (unit and integration) run in the development, staging, and production stages of the MLOps workflow

    Key concept

    Unit test — A test that gives one self-contained piece of code, usually a single function, specific inputs and checks that it returns the expected output. It does not check how that function works with other pipelines.

    1.What a unit test checks, and how to write a function worth testing

    On Databricks, unit testing means testing self-contained pieces of code, usually individual functions, early and often. The aim is to find problems and mistaken assumptions sooner. The Databricks guide on this topic sets a clear boundary. It covers unit tests for functions and says it does not cover integration, system, acceptance, or performance testing. Keep that boundary in mind for the exam. A unit test calls one function with specific inputs and asserts on what comes back. Checking whether whole pipelines work together is a separate kind of test.

    The design rule is simple. To get the best results, a function should return one predictable outcome of one data type. An existence check should return a boolean. A row count should return a non-negative whole number. The docs name the anti-pattern directly: a function that returns false when something is missing but returns the thing itself when it exists. A count function that returns false when there are no rows is the same mistake. The example functions below follow the rule. They live in a plain myfunctions.py file in a Git folder rather than in a notebook. Because of that, the file has to build its own Spark session, which a notebook would normally create for you.

    Two example functions from myfunctions.py. Each takes its inputs as arguments and returns one type: a boolean or a count.python
    # Does the specified column exist in the given DataFrame?
    def columnExists(dataFrame, columnName):
      if columnName in dataFrame.columns:
        return True
      else:
        return False
    
    # How many rows are there for the specified value in the specified column
    # in the given DataFrame?
    def numRowsInColumnForValue(dataFrame, columnName, columnValue):
      df = dataFrame.filter(col(columnName) == columnValue)
    
      return df.count()

    Unit tests are one of two kinds of test in the Databricks MLOps workflow. The other is the integration test, and the two run at different points. In the development stage, the focus is experimentation, and you write the code and its unit tests. The staging stage is where the ML pipeline code is tested to make sure it is ready for production. ML engineers build a CI pipeline for it, and a pull request triggers it. The CI pipeline first builds the source code and runs unit tests. If they fail, the pull request is rejected. Then it runs integration tests. An integration test runs all the pipelines together, including feature engineering, model training, inference, and monitoring, and checks that they function correctly together. That is the difference from a unit test, which checks one function in isolation. The staging environment should match production as closely as is reasonable, and it has its own catalog in Unity Catalog. Assets written there are generally temporary. Integration tests can trade fidelity for speed or cost, for example by training on small subsets of data. They can also be scheduled periodically on the main branch. The production stage does not repeat that testing. The release branch only starts the production stage after all CI tests have passed. Keep unit tests away from production data, because they should run against non-production data, for example views instead of tables.

    Checkpoint 1 of 4· Exam question

    A data scientist has written a `compute_feature_ratio` function directly inside a Databricks Python notebook cell. The team wants to add a pytest suite that asserts the function returns the expected value for a set of known inputs, and they also want other notebooks and a scheduled job to be able to call the same function without duplicating the code. What should they do first?

    Sources123

    2.Writing and running unit tests in a notebook

    Databricks recommends writing and running unit tests in a notebook. You can run some commands in the web terminal, but it has more limitations, including no Spark support. Many unit testing libraries work directly in a notebook cell. The built-in Python unittest package is one example. In the sample below, the test class inherits from unittest.TestCase and the test method compares a known input with a known output. The unittest.main(argv=[''], verbosity=2, exit=False) call runs the tests inside the cell. The final assert on wasSuccessful() makes the cell itself fail when any test fails.

    A function and its unittest test case run in the same notebookpython
    def reverse(s):
        return s[::-1]
    
    import unittest
    
    class TestHelpers(unittest.TestCase):
        def test_reverse(self):
            self.assertEqual(reverse('abc'), 'cba')
    
    r = unittest.main(argv=[''], verbosity=2, exit=False)
    assert r.result.wasSuccessful(), 'Test failed; see logs above'

    Test failures show up in the cell's output area. You can hide test code or results from the cell actions menu, and errors are still displayed even when results are hidden. A widget lets one notebook switch between running its tests and doing its normal work. A Mode dropdown chooses the branch.

    Checkpoint 2 of 4· Fill the gap

    Which widget call reads the current Mode so the notebook runs its tests only in Test mode?

    dbutils.widgets.dropdown("Mode", "Test", ["Test", "Normal"])
    
    def reverse(s):
      return s[::-1]
    
    if dbutils.widgets. ? ('Mode') == 'Test':
      assert reverse('abc') == 'cba'
      print('Tests passed')
    else:
      print(reverse('desrever'))

    For tests stored in Python files, the workspace has a testing sidebar, inline run controls, and a results pane. Discovery uses pytest naming conventions. A file counts as a test file only if its name matches test_*.py or *_test.py. Inside a test file, a test is a test-prefixed function outside a class, or a test-prefixed method inside a Test-prefixed class that has no __init__ method. Static and class methods inside Test classes are discovered too. A function called verify_output is never collected, however sensible its name looks.

    Test cases that pytest-style discovery finds: methods in a Test-prefixed class and a test-prefixed functionpython
    class TestClass():
        def test_1(self):
            assert True
    
        def test_3(self):
            assert 4 == 3
    
    def test_foo():
        assert "foo" == "bar"

    Checkpoint 3 of 4· Check yourself

    Which of these will the workspace testing pane discover as a test case?

    You can also run tests without anyone starting them. A scheduled notebook job can run them on a regular basis and email the address you specify. For notebooks in a Databricks Git folder, you can set up a CI/CD-style workflow in which the notebook tests run on every commit, for example with Databricks GitHub Actions.

    Sources145

    3.Organizing functions and their unit tests

    Where you put a function and where you put its tests both matter. That choice decides who can reuse the function and which tools can test it. For Python, R, and Scala notebooks, Databricks describes three common layouts, each with its own benefits and challenges. SQL notebooks follow a separate pattern.

    Ways to organize functions and their unit tests: benefits, challenges, and where Databricks recommends each
    ApproachBenefitsChallengesRecommended for
    Functions and unit tests outside notebooks (files)Callable from notebooks and outside them; test frameworks are better designed to run outside notebooks; workspace tools discover and run Python testsNot supported for Scala notebooks; more files to track and maintainPython and R notebooks
    Functions in one notebook, unit tests in a separate notebookFunctions are easier to reuse across notebooksMore notebooks to track; functions cannot be used outside notebooks and are harder to test outside themScala notebooks
    Functions and unit tests in the same notebookOne notebook makes tracking and maintenance easierHarder to reuse across notebooks; cannot be used outside notebooks; harder to test outside themNo language-specific recommendation
    SQL user-defined functions (SQL UDFs) stored in schemasFunctions and their unit tests can be called from SQL notebooksNone listed in the sourceSQL notebooks

    The separate-notebook layout depends on %run. The test notebook's first cell runs %run ./shared-code-notebook, which loads reverse into the test notebook. A later cell then holds the unittest class. Databricks Git folders give you the file-based alternative: test code can live in non-notebook source files and still be called from a notebook.

    Checkpoint 4 of 4· Match them up

    Match each way of organizing code to its defining trade-off or recommendation

    Tap a term, then the definition that fits it.

    Sources14

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.pytest will run any function in a test file, whatever its name, such as verify_nulls or check_output.Why is that wrong?

      Discovery follows pytest naming conventions. Test files must match test_*.py or *_test.py, and only test-prefixed functions, or test-prefixed methods in Test-prefixed classes without __init__, are collected.

      Covered in Writing and running unit tests in a notebook

    2. 2.Storing functions and their tests in files outside notebooks is the best practice for every notebook language.Why is that wrong?

      That approach is not supported for Scala notebooks. For Scala, Databricks recommends functions in one notebook and their tests in a separate notebook.

      Covered in Organizing functions and their unit tests

    3. 3.Unit tests are the right way to check that feature engineering, training, and inference work together, and they should run against production data.Why is that wrong?

      A unit test checks one function. Integration tests run the pipelines together in staging. Unit tests should run against non-production data, not production data.

      Covered in What a unit test checks, and how to write a function worth testing

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “Unit testing is an approach to testing self-contained units of code, such as functions, early and often.”
      ↩︎ What a unit test checks, and how to write a function worth testing
      “a function should return a single predictable outcome and be of a single data type.”
      ↩︎ What a unit test checks, and how to write a function worth testing
      “This article also does not cover other kinds of testing methods, such as integration testing”
      ↩︎ What a unit test checks, and how to write a function worth testing
      “it is a best practice to not run unit tests against functions that work with data in production.”
      ↩︎ What a unit test checks, and how to write a function worth testing
      “Databricks recommends writing and running your unit tests in a notebook.”
      ↩︎ Writing and running unit tests in a notebook
      “For Python and R notebooks, Databricks recommends storing functions and their unit tests outside of notebooks.”
      ↩︎ Organizing functions and their unit tests
      “store functions as SQL user-defined functions (SQL UDFs) in your schemas (also known as databases).”
      ↩︎ Organizing functions and their unit tests
      “Unit testing is an approach to testing self-contained units of code, such as functions, early and often.”
      ↩︎ Key concept
      “Challenges: This approach is not supported for Scala notebooks.”
      ↩︎ Exam trap 2
      “it is a best practice to not run unit tests against functions that work with data in production.”
      ↩︎ Exam trap 3
      “For Scala notebooks, Databricks recommends including functions in one notebook and their unit tests in a separate notebook.”
      ↩︎ Prediction
    2. 2.
      “ML engineers create a CI pipeline to implement the unit and integration tests run in this stage.”
      ↩︎ What a unit test checks, and how to write a function worth testing
      “Integration tests run all pipelines (including feature engineering, model training, inference, and monitoring) to ensure that they function correctly together.”
      ↩︎ What a unit test checks, and how to write a function worth testing
      “You can schedule periodic integration tests on the main branch.”
      ↩︎ What a unit test checks, and how to write a function worth testing
    3. 3.
      “PRs trigger unit tests and integration tests in an isolated staging Databricks workspace.”
      ↩︎ What a unit test checks, and how to write a function worth testing
    4. 4.
      “Test failures appear in the output area of the cell.”
      ↩︎ Writing and running unit tests in a notebook
      “You can use widgets to distinguish test invocations from normal invocations in a single notebook.”
      ↩︎ Writing and running unit tests in a notebook
      “you can set up a CI/CD-style workflow by configuring notebook tests to run for each commit.”
      ↩︎ Writing and running unit tests in a notebook
      “When you use %run, test code is included in a separate notebook that you call from another notebook.”
      ↩︎ Organizing functions and their unit tests
    5. 5.
      “Databricks follows pytest naming conventions to detect test files, classes and cases.”
      ↩︎ Writing and running unit tests in a notebook
      “test-prefixed functions or methods inside Test-prefixed classes (without an __init__ method)”
      ↩︎ Exam trap 1

    Continue to page 2 of 2

    Integration Testing ML Pipelines Across Dev, Staging, and Production

    Spotted a mistake, or was something unclear? Tell us.