You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Nutter framework makes it easy to test Databricks notebooks. The framework enables a simple inner dev loop, but also easily integrates with Azure DevOps Build/Release pipelines, among others. When data or ML engineers want to test a notebook, they simply create a test notebook called *test_*<notebook_under_test>.
7
27
28
+
The Nutter framework makes it easy to test Databricks notebooks. The framework enables a simple inner dev loop and easily integrates with Azure DevOps Build/Release pipelines, among others. When data or ML engineers want to test a notebook, they simply create a test notebook called *test_*<notebook_under_test>.
29
+
30
+
Nutter has 2 main components:
31
+
32
+
1. Nutter Runner - this is the server-side component that is installed as a library on the Databricks cluster
33
+
2. Nutter CLI - this is the client CLI that can be installed both on a developers laptop and on a build agent
8
34
9
35
The tests can be run from within that notebook or executed from the Nutter CLI, useful for integrating into Build/Release pipelines.
10
36
37
+
## Nutter Runner
38
+
39
+
### Cluster Installation
40
+
41
+
The Nutter Runner can be installed as a cluster library, via PyPI.
42
+
43
+
For more information about installing libraries on a cluster, review [Install a library on a cluster](https://docs.microsoft.com/en-us/azure/databricks/libraries#--install-a-library-on-a-cluster).
44
+
45
+
### Nutter Fixture
46
+
47
+
The Nutter Runner is simply a base Python class, NutterFixture, that test fixtures implement. The runner runtime is a module you can use once you install Nutter on the Databricks cluster. The NutterFixture base class can then be imported in a test notebook and implemented by a test fixture:
48
+
49
+
```Python
50
+
from runtime.nutterfixture import NutterFixture, tag
51
+
classMyTestFixture(NutterFixture):
52
+
…
53
+
```
54
+
55
+
To run the tests:
56
+
57
+
```Python
58
+
result = MyTestFixture().execute_tests()
59
+
```
60
+
61
+
To view the results from within the test notebook:
62
+
63
+
```Python
64
+
print(result.to_string())
65
+
```
66
+
67
+
To return the test results to the Nutter CLI:
68
+
69
+
```Python
70
+
result.exit(dbutils)
71
+
```
72
+
73
+
__Note:__ The call to result.exit, behind the scenes calls dbutils.notebook.exit, passing the serialized TestResults back to the CLI. At the current time, print statements do not work when dbutils.notebook.exit is called in a notebook, even if they are written prior to the call. For this reason, it is required to *temporarily* comment out result.exit(dbutils) when running the tests locally.
74
+
11
75
The following defines a single test fixture named 'MyTestFixture' that has 1 TestCase named 'test_name':
76
+
12
77
```Python
13
78
from runtime.nutterfixture import NutterFixture, tag
14
79
classMyTestFixture(NutterFixture):
@@ -28,6 +93,7 @@ result.exit(dbutils)
28
93
```
29
94
30
95
To execute the test from within the test notebook, simply run the cell containing the above code. At the current time, in order to see the below test result, you will have to comment out the call to result.exit(dbutils). That call is required to send the results, if the test is run from the CLI, so do not forget to uncomment after locally testing.
1. Nutter Runner - this is the server-side component that is installed as a library on the Databricks cluster
45
-
2. Nutter CLI - this is the client CLI that can be installed both on a developers laptop and on a build agent
108
+
### Test Cases
46
109
47
-
## Nutter Runner
48
-
The Nutter Runner is simply a base Python class, NutterFixture, that test fixtures implement. The runner is installed as a library on the Databricks cluster. The NutterFixture base class can then be imported in a test notebook and implemented by a test fixture:
49
-
```Python
50
-
from runtime.nutterfixture import NutterFixture, tag
51
-
classMyTestFixture(NutterFixture):
52
-
…
53
-
```
110
+
A test fixture can contain 1 or mote test cases. Test cases are discovered when execute_tests() is called on the test fixture. Every test case is comprised of 2 required and 2 optional methods and are discovered by the following convention: prefix_testname, where valid prefixes are: before_, run_, assertion_, and after_. A test fixture that has run_fred and assertion_fred methods has 1 test case called 'fred'. The following are details about test case methods:
54
111
55
-
To run the tests:
56
-
```Python
57
-
result = MyTestFixture().execute_tests()
58
-
```
112
+
*_before\_(testname)_ - (optional) - if provided, is run prior to the 'run_' method. This method can be used to setup any test pre-conditions
59
113
60
-
To view the results from within the test notebook:
61
-
```Python
62
-
print(result.to_string())
63
-
```
114
+
*_run\_(testname)_ - (required) - run after 'before_' if before was provided, otherwise run first. This method typically runs the notebook under test
64
115
65
-
To return the test results to the Nutter CLI:
66
-
```Python
67
-
result.exit(dbutils)
68
-
```
116
+
*_assertion\_(testname)_ (required) - run after 'run_'. This method typically contains the test assertions
69
117
70
-
__Note:__The call to result.exit, behind the scenes calls dbutils.notebook.exit, passing the serialized TestResults back to the CLI. At the current time, print statements do not work when dbutils.notebook.exit is called in a notebook, even if they are written prior to the call. For this reason, it is required to *temporarily* comment out result.exit(dbutils) when running the tests locally.
118
+
__Note:__ You can assert test scenarios using the standard ``` assert ``` statement or the assertion capabilities from a package of your choice.
71
119
72
-
### Test Cases
73
-
A test fixture can contain 1 or mote test cases. Test cases are discovered when execute_tests() is called on the test fixture. Every test case is comprised of 2 required and 2 optional methods and are discovered by the following convention: prefix_testname, where valid prefixes are: before_, run_, assertion_, and after_. A test fixture that has run_fred and assertion_fred methods has 1 test case called 'fred'. The following are details about test case methods:
74
-
75
-
* before_(testname) - (optional) - if provided, is run prior to the 'run_' method. This method can be used to setup any test pre-conditions
76
-
* run_(testname) - (required) - run after 'before_' if before was provided, otherwise run first. This method typically runs the notebook under test
77
-
* assertion_(testname) (required) - run after 'run_'. This method typically contains the test assertions
78
-
* after_(testname) (optional) - if provided, run after 'assertion_'. This method typically is used to clean up any test data used by the test
120
+
*_after\_(testname)_ (optional) - if provided, run after 'assertion_'. This method typically is used to clean up any test data used by the test
79
121
80
122
A test fixture can have multiple test cases. The following example shows a fixture called MultiTestFixture with 2 test cases: 'test_case_1' and 'test_case_2' (assertion code omitted for brevity):
123
+
81
124
```Python
82
125
from runtime.nutterfixture import NutterFixture, tag
83
126
classMultiTestFixture(NutterFixture):
@@ -95,11 +138,13 @@ class MultiTestFixture(NutterFixture):
95
138
96
139
result = MultiTestFixture().execute_tests()
97
140
print(result.to_string())
98
-
result.exit(dbutils)
141
+
#result.exit(dbutils)
99
142
```
100
143
101
144
### before_all and after_all
145
+
102
146
Test Fixtures also can have a before_all() method which is run prior to all tests and an after_all() which is run after all tests.
147
+
103
148
```Python
104
149
from runtime.nutterfixture import NutterFixture, tag
105
150
classMultiTestFixture(NutterFixture):
@@ -116,41 +161,31 @@ class MultiTestFixture(NutterFixture):
116
161
…
117
162
```
118
163
119
-
### Installing the Nutter Runner on Azure Databricks
120
-
Perform the following steps to install the Nutter wheel file on your Azure Databricks cluster:
121
-
1. Open your Azure Databricks workspace
122
-
2. Click on the 'Clusters' link (on the left)
123
-
3. Click on the cluster you wish to install Nutter on
124
-
4. Click 'Libraries' (at the top)
125
-
5. Click 'Install New'
126
-
6. Drag the Nutter whl file
127
-
128
164
## Nutter CLI
129
165
130
-
###
131
-
### Getting Started
132
-
Install the Nutter CLI from the source.
166
+
The Nutter CLI is a command line interface that allows you to execute and list tests via a Command Prompt.
__Note:__ It's recommended to install the Nutter CLI in a virtual environment.
144
177
145
178
Set the environment variables.
146
179
147
-
Linux
180
+
Linux
181
+
148
182
```bash
149
183
export DATABRICKS_HOST=<HOST>
150
184
export DATABRICKS_TOKEN=<TOKEN>
151
185
```
152
186
153
187
Windows PowerShell
188
+
154
189
```cmd
155
190
$env DATABRICKS_HOST="HOST"
156
191
$env DATABRICKS_TOKEN="TOKEN"
@@ -183,11 +218,13 @@ nutter list /dataload --recursive
183
218
The ```run``` command schedules the execution of test notebooks and waits for their result.
184
219
185
220
### Run single test notebook
221
+
186
222
The following command executes the test notebook ```/dataload/test_sourceLoad``` in the cluster ```0123-12334-tonedabc```.
187
223
188
224
```bash
189
225
nutter run dataload/test_sourceLoad --cluster_id 0123-12334-tonedabc
190
226
```
227
+
191
228
__Note:__ In Azure Databricks you can get the cluster ID by selecting a cluster name from the Clusters tab and clicking on the JSON view.
192
229
193
230
### Run multiple tests notebooks
@@ -225,9 +262,9 @@ __Note:__ Running tests notebooks in parallel introduces the risk of data race c
225
262
226
263
## Nutter CLI Syntax and Flags
227
264
228
-
*Run Command*
265
+
### Run Command
229
266
230
-
```
267
+
```bash
231
268
SYNOPSIS
232
269
nutter run TEST_PATTERN CLUSTER_ID <flags>
233
270
@@ -236,20 +273,20 @@ POSITIONAL ARGUMENTS
236
273
CLUSTER_ID
237
274
```
238
275
239
-
```
276
+
```bash
240
277
FLAGS
241
278
--timeout Execution timeout. Default 120s
242
279
--junit_report Create a JUnit XML report from the test results.
243
280
--tags_report Create a CSV report from the test results that includes the test cases tags.
244
281
--max_parallel_tests Sets the level of parallelism fortest notebook execution.
245
282
--recursive Executes all tests in the hierarchical folder structure.
246
-
```
283
+
```
247
284
248
285
__Note:__ You can also use flags syntax for POSITIONAL ARGUMENTS
249
286
250
-
*List Command*
287
+
### List Command
251
288
252
-
```
289
+
```bash
253
290
NAME
254
291
nutter list
255
292
@@ -260,7 +297,7 @@ POSITIONAL ARGUMENTS
260
297
PATH
261
298
```
262
299
263
-
```
300
+
```bash
264
301
FLAGS
265
302
--recursive Lists all tests in the hierarchical folder structure.
266
303
```
@@ -271,19 +308,117 @@ __Note:__ You can also use flags syntax for POSITIONAL ARGUMENTS
271
308
272
309
You can run the Nutter CLI within an Azure DevOps pipeline. The Nutter CLI will exit with non-zero code when a test case fails or the execution of the test notebook is not successful.
273
310
274
-
For full integration of the test results with Azure DevOps you can set the flag ```--junit_report```. When this flag is set, the Nutter CLI outputs the results of the tests cases as a JUnit XML compliant file.
311
+
The following Azure DevOps pipeline installs nutter, recursively executes all tests in the workspace folder ```/Shared/ ``` and publishes the test results.
312
+
313
+
__Note:__ The pipeline expects the Databricks cluster, host and API token as pipeline varibles.
314
+
315
+
316
+
317
+
```yaml
318
+
# Starter Nutter pipeline
319
+
320
+
trigger:
321
+
- develop
322
+
323
+
pool:
324
+
vmImage: 'ubuntu-latest'
325
+
326
+
steps:
327
+
- task: UsePythonVersion@0
328
+
inputs:
329
+
versionSpec: '3.5'
330
+
331
+
- script: |
332
+
pip install nutter
333
+
displayName: 'Install Nutter'
334
+
335
+
- script: |
336
+
nutter run /Shared/ $CLUSTER --recursive --junit_report
337
+
displayName: 'Execute Nutter'
338
+
env:
339
+
CLUSTER: $(clusterID)
340
+
DATABRICKS_HOST: $(databricks_host)
341
+
DATABRICKS_TOKEN: $(databricks_token)
342
+
343
+
- task: PublishTestResults@2
344
+
inputs:
345
+
testResultsFormat: 'JUnit'
346
+
testResultsFiles: '**/test-*.xml'
347
+
testRunTitle: 'Publish Nutter results'
348
+
```
349
+
350
+
In some scenarios, the notebooks under tests must be executed in a pre-configured test workspace, other than the development one, that contains the necessary pre-requisites such as test data, tables or mounted points. In such scenarios, you can use the pipeline to deploy the notebooks to the test workspace before executing the tests with Nutter.
351
+
352
+
The following sample pipeline uses the Databricks CLI to publish the notebooks from triggering branch to the test workspace.
353
+
354
+
355
+
```yaml
356
+
# Starter Nutter pipeline
357
+
358
+
trigger:
359
+
- develop
360
+
361
+
pool:
362
+
vmImage: 'ubuntu-latest'
363
+
364
+
steps:
365
+
- task: UsePythonVersion@0
366
+
inputs:
367
+
versionSpec: '3.5'
368
+
369
+
- task: configuredatabricks@0
370
+
displayName: 'Configure Databricks CLI'
371
+
inputs:
372
+
url: $(databricks_host)
373
+
token: $(databricks_token)
374
+
375
+
- task: deploynotebooks@0
376
+
displayName: 'Publish notebooks to test workspace'
nutter run /Shared/ $CLUSTER --recursive --junit_report
387
+
displayName: 'Execute Nutter'
388
+
env:
389
+
CLUSTER: $(clusterID)
390
+
DATABRICKS_HOST: $(databricks_host)
391
+
DATABRICKS_TOKEN: $(databricks_token)
392
+
393
+
- task: PublishTestResults@2
394
+
inputs:
395
+
testResultsFormat: 'JUnit'
396
+
testResultsFiles: '**/test-*.xml'
397
+
testRunTitle: 'Publish Nutter results'
398
+
```
399
+
400
+
## Contributing
401
+
402
+
### Contribution Tips
403
+
404
+
- There's a known issue with VS Code and the lastest version of pytest.
405
+
- Please make sure that you install pytest 5.0.1
406
+
- If you installed pytest using VS Code, then you are likely using the incorrect version. Run the following command to fix it:
275
407
276
-
# Contributing
277
-
## Using VS Code
278
-
- There's a known issue with VS Code and the lastest version of pytest.
279
-
- Please make sure that you install pytest 5.0.1
280
-
- If you installed pytest using VS Code, then you are likely using the incorrect version. Run the following command to fix it:
281
408
```Python
282
409
pip install --force-reinstall pytest==5.0.1
283
410
```
284
411
285
-
## Creating the wheel file and manually test wheel locally
412
+
Creating the wheel file and manually test wheel locally
413
+
286
414
1. Change directory to the root that contains setup.py
287
415
2. Update the version in the setup.py
288
416
3. Run the following command: python3 setup.py sdist bdist_wheel
289
417
4. (optional) Install the wheel locally by running: python3 -m pip install <path-to-whl-file>
418
+
419
+
### Contribution Guidelines
420
+
421
+
If you would like to become an active contributor to this project please follow the instructions provided in [Microsoft Azure Projects Contribution Guidelines](http://azure.github.io/guidelines/).
422
+
423
+
-----
424
+
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [[email protected]](mailto:[email protected]) with any additional questions or comments.
0 commit comments