Skip to content

Commit 237ed26

Browse files
feature(testing): --test-changed-only on plan and --select-model on test command
Signed-off-by: Cortland Goffena <30168413+cmgoffena13@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 813fe54 commit 237ed26

17 files changed

Lines changed: 206 additions & 155 deletions

File tree

docs/concepts/overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Bad data is worse than no data. The best way to keep bad data out of your system
5252
### [Tests](./tests.md)
5353
SQLMesh "tests" are similar to unit tests in software development, where the unit is a single model. SQLMesh tests validate model *code* &mdash; you specify the input data and expected output, then SQLMesh runs the test and compares the expected and actual output.
5454

55-
SQLMesh automatically runs tests for models included in a `plan` (added, modified, or restated). Plans with no such models skip unit tests by default. Use `--all-tests` for the full suite, `--skip-tests` to skip, or run tests on demand with the [`test` command](../reference/cli.md#test).
55+
SQLMesh automatically runs all unit tests when a `plan` is created. Use `--test-changed-only` to run tests only for models included in the plan (added, modified, or restated), `--skip-tests` to skip, run tests for specific models with [`sqlmesh test --select-model`](../reference/cli.md#test), or run the full suite on demand with the [`test` command](../reference/cli.md#test).
5656

5757
Learn more in the [testing guide](../guides/testing.md).
5858

docs/concepts/tests.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Testing allows you to protect your project from regression by continuously verifying that the output of each model matches your expectations. Unlike [audits](audits.md), tests are executed either on demand (for example, as part of a CI/CD job or via [`sqlmesh test`](../reference/cli.md#test)) or when a new [plan](plans.md) is created.
44

5-
By default, `sqlmesh plan` runs unit tests only for models included in the plan (added, modified, or restated). Plans with no such models skip unit tests. Use `--all-tests` to run the full suite, or `--skip-tests` to run none.
5+
By default, `sqlmesh plan` runs all unit tests. Use `--test-changed-only` to run tests only for models included in the plan (added, modified, or restated), or `--skip-tests` to run none. With both `--select-model` and `--test-changed-only`, tests run only for selected models that changed.
66

77
Similar to unit testing in software development, SQLMesh evaluates the model's logic against predefined inputs and then compares the output to expected outcomes provided as part of each test.
88

docs/faq/faq.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
SQLMesh's default behavior is appropriate for most deployments, but you can override where SQLMesh creates physical tables and views with [schema configuration options](../guides/configuration.md#environment-schemas).
103103

104104
??? question "What's the difference between a `test` and an `audit`?"
105-
A SQLMesh [`test`](../concepts/tests.md) is analogous to a "unit test" in software engineering. It tests *code* based on known inputs and outputs. In SQLMesh, the inputs and outputs are specified in a YAML file, and SQLMesh runs tests for models included in the plan when `sqlmesh plan` is executed (use `--all-tests` for the full suite).
105+
A SQLMesh [`test`](../concepts/tests.md) is analogous to a "unit test" in software engineering. It tests *code* based on known inputs and outputs. In SQLMesh, the inputs and outputs are specified in a YAML file, and SQLMesh runs all unit tests when `sqlmesh plan` is executed (use `--test-changed-only` to run only tests for models in the plan).
106106

107107
Writing YAML is annoying and error-prone, so SQLMesh's [`create_test` command](../concepts/tests.md#automatic-test-generation) allows you to automatically generate YAML test files based on queries of existing data tables.
108108

docs/guides/models.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ Reverting to a previous model version is a quick operation since no additional w
161161
162162
SQLMesh automatically validates your models in order to ensure the quality and accuracy of your data. This is done via the following:
163163
164-
* Running unit tests for models in the plan when you execute the `plan` command (use `--all-tests` for the full suite, or `--skip-tests` to skip). This ensures changes applied to any environment are logically validated. Refer to [testing](../concepts/tests.md) for more information.
164+
* Running all unit tests when you execute the `plan` command (use `--test-changed-only` to run only tests for models in the plan, or `--skip-tests` to skip). This ensures changes applied to any environment are logically validated. Refer to [testing](../concepts/tests.md) for more information.
165165
* Running audits whenever data is loaded to a table (either for backfill or loading on a cadence). This way you know all data present in any table has passed all defined audits. Refer to [auditing](../concepts/audits.md) for more information.
166166
167167
SQLMesh also provides automatic validation via CI/CD by automatically creating a preview environment.

docs/guides/testing.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,19 @@ OK
1212
```
1313
As the unit tests run, SQLMesh will identify any that fail.
1414

15-
By default, `sqlmesh plan` runs unit tests only for models included in the plan (added, modified, or restated). Plans with no such models skip unit tests. Use `--all-tests` to run the full suite, or `--skip-tests` to run none.
15+
By default, `sqlmesh plan` runs all unit tests. Use `--test-changed-only` to run tests only for models included in the plan (added, modified, or restated), or `--skip-tests` to run none. With both `--select-model` and `--test-changed-only`, tests run only for selected models that changed.
1616

1717
For more information about tests, refer to [testing](../concepts/tests.md).
1818

1919
### Test changes to a specific model
2020

21-
To run a specific model test, pass in the suite file name followed by `::` and the name of the test; for example: `sqlmesh test tests/test_suite.yaml::test_example_full_model`.
21+
To run unit tests for a specific model, use `--select-model`:
22+
23+
```bash
24+
$ sqlmesh test --select-model sqlmesh_example.full_model
25+
```
26+
27+
Alternatively, pass in the suite file name followed by `::` and the name of the test; for example: `sqlmesh test tests/test_suite.yaml::test_example_full_model`.
2228

2329
### Run a subset of tests
2430

docs/reference/cli.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,8 @@ Options:
357357
Default: prod.
358358
--skip-tests Skip tests prior to generating the plan if
359359
they are defined.
360-
--all-tests Run all unit tests instead of only tests for
361-
models included in the plan.
360+
--test-changed-only Run unit tests only for models included in
361+
the plan instead of all tests.
362362
--skip-linter Skip linting prior to generating the plan if
363363
the linter is enabled.
364364
-r, --restate-model TEXT Restate data for specified models and models
@@ -628,6 +628,8 @@ Options:
628628
-v, --verbose Verbose output.
629629
--preserve-fixtures Preserve the fixture tables in the testing database,
630630
useful for debugging.
631+
--select-model TEXT Select specific models to run unit tests for. Can be
632+
specified multiple times.
631633
--help Show this message and exit.
632634
```
633635

docs/reference/notebook.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ options:
9696
#### plan
9797
```
9898
%plan [--start START] [--end END] [--execution-time EXECUTION_TIME]
99-
[--create-from CREATE_FROM] [--skip-tests] [--all-tests]
99+
[--create-from CREATE_FROM] [--skip-tests] [--test-changed-only]
100100
[--restate-model [RESTATE_MODEL ...]] [--no-gaps]
101101
[--skip-backfill, --dry-run] [--forward-only]
102102
[--effective-from EFFECTIVE_FROM] [--no-prompts] [--auto-apply]
@@ -120,8 +120,8 @@ options:
120120
The environment to create the target environment from
121121
if it doesn't exist. Default: prod.
122122
--skip-tests, -t Skip the unit tests defined for the model.
123-
--all-tests Run all unit tests instead of only tests for models
124-
included in the plan.
123+
--test-changed-only Run unit tests only for models included in the plan
124+
instead of all tests.
125125
--restate-model <[RESTATE_MODEL ...]>, -r <[RESTATE_MODEL ...]>
126126
Restate data for specified models (and models
127127
downstream from the one specified). For production
@@ -433,7 +433,8 @@ options:
433433

434434
#### run_test
435435
```
436-
%run_test [--pattern [PATTERN ...]] [--verbose] [--preserve-fixtures] [tests ...]
436+
%run_test [--pattern [PATTERN ...]] [--verbose] [--preserve-fixtures]
437+
[--select-model [SELECT_MODEL ...]] [tests ...]
437438
438439
Run unit test(s).
439440
@@ -446,6 +447,8 @@ options:
446447
--verbose, -v Verbose output.
447448
--preserve-fixtures Preserve the fixture tables in the testing database,
448449
useful for debugging.
450+
--select-model <[SELECT_MODEL ...]>
451+
Select specific models to run unit tests for.
449452
```
450453

451454
#### audit

sqlmesh/cli/main.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -424,9 +424,9 @@ def diff(ctx: click.Context, environment: t.Optional[str] = None) -> None:
424424
default=None,
425425
)
426426
@click.option(
427-
"--all-tests",
427+
"--test-changed-only",
428428
is_flag=True,
429-
help="Run all unit tests instead of only tests for models included in the plan.",
429+
help="Run unit tests only for models included in the plan instead of all tests.",
430430
default=None,
431431
)
432432
@click.option(
@@ -801,6 +801,12 @@ def create_test(
801801
default=False,
802802
help="Preserve the fixture tables in the testing database, useful for debugging.",
803803
)
804+
@click.option(
805+
"--select-model",
806+
type=str,
807+
multiple=True,
808+
help="Select specific models to run unit tests for.",
809+
)
804810
@click.argument("tests", nargs=-1)
805811
@click.pass_obj
806812
@error_handler
@@ -810,14 +816,19 @@ def test(
810816
k: t.List[str],
811817
verbose: int,
812818
preserve_fixtures: bool,
819+
select_model: t.List[str],
813820
tests: t.List[str],
814821
) -> None:
815822
"""Run model unit tests."""
823+
model_names = (
824+
obj._new_selector().expand_model_selections(select_model) if select_model else None
825+
)
816826
result = obj.test(
817827
match_patterns=k,
818828
tests=tests,
819829
verbosity=Verbosity(verbose),
820830
preserve_fixtures=preserve_fixtures,
831+
model_names=model_names,
821832
)
822833
if not result.wasSuccessful():
823834
exit(1)

sqlmesh/cli/project_init.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,25 @@ def _gen_example_objects(schema_name: str) -> ExampleObjects:
286286
num_orders: 1
287287
"""
288288

289+
tests["test_incremental_model"] = f"""test_example_incremental_model:
290+
model: {incremental_model_name}
291+
vars:
292+
start: 2020-01-01
293+
end: 2020-01-02
294+
inputs:
295+
{seed_model_name}:
296+
rows:
297+
- id: 1
298+
item_id: 1
299+
event_date: 2020-01-01
300+
outputs:
301+
query:
302+
rows:
303+
- id: 1
304+
item_id: 1
305+
event_date: 2020-01-01
306+
"""
307+
289308
return ExampleObjects(
290309
sql_models=sql_models,
291310
python_models=python_models,

sqlmesh/core/console.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2232,6 +2232,9 @@ def log_test_results(self, result: ModelTextTestResult, target_dialect: str) ->
22322232
message = (
22332233
f"Ran {result.testsRun} tests against {target_dialect} in {result.duration} seconds."
22342234
)
2235+
if result.tests_skipped:
2236+
message = f"{message}\nSkipped {result.tests_skipped} tests"
2237+
22352238
if result.wasSuccessful():
22362239
self._print("=" * divider_length)
22372240
self._print(
@@ -3160,6 +3163,8 @@ def log_test_results(self, result: ModelTextTestResult, target_dialect: str) ->
31603163
message = (
31613164
f"Ran {result.testsRun} tests against {target_dialect} in {result.duration} seconds."
31623165
)
3166+
if result.tests_skipped:
3167+
message = f"{message}\nSkipped {result.tests_skipped} tests"
31633168

31643169
if result.wasSuccessful():
31653170
success_color = {"color": "#008000"}
@@ -3600,6 +3605,8 @@ def log_test_results(self, result: ModelTextTestResult, target_dialect: str) ->
36003605
return
36013606

36023607
message = f"Ran `{result.testsRun}` Tests Against `{target_dialect}`"
3608+
if result.tests_skipped:
3609+
message = f"{message}\n**Skipped `{result.tests_skipped}` Tests**"
36033610

36043611
if result.wasSuccessful():
36053612
self._print(f"**Successfully {message}**\n\n")

0 commit comments

Comments
 (0)