---
name: mps-tests
description: Use when writing or modifying tests inside MPS `@tests` models — `NodesTestCase` (typesystem, constraints, scopes, dataflow, generator output), `EditorTestCase` (intentions, actions, keystrokes, side-transforms, completion), `MigrationTestCase` (migration scripts), `BTestCase` (plain JUnit on hand-written Java/Kotlin runtime), inline annotations such as `has error` / `has type` / `ScopesTest`, label-based `node<label>` cross-references, caret markers, and running tests via MCP / in MPS. Reach for this skill whenever the task involves authoring or fixing tests in a `tests`-stereotype model, or interpreting failures from one.
type: reference
---

# Writing Tests in MPS Models

## Loading companion skills

Companion names in this skill are lazy dependencies: load only those relevant to the current task. If this skill came from an MCP server, use the host's skill loader to resolve the companion's unique discovered entry URI on the same host-assigned originating server. If the host has no server-backed skill loader, stop and report that limitation; do not silently fall back to a filesystem copy. If this skill came from a filesystem catalog, load the named sibling from that same catalog at `<skills-root>/<skill-name>/SKILL.md`, even if remote skill loaders are also available. Do not invent a tool name or server endpoint.

MPS ships a dedicated **test language** — `jetbrains.mps.lang.test` — for testing language artefacts (typesystem, scopes, constraints, dataflow, editor, generator, migrations). Plain JUnit-style tests against runtime classes use `jetbrains.mps.baseLanguage.unitTest` (the `BTestCase` concept).

A test is a **root node** living in a model whose **stereotype is `tests`** (pass `moduleName: "<hosting solution>"` and `modelName: "<name>@tests"` to `mps_mcp_create_model`; the on-disk file ends in `@tests.mps`). See [aspect-model-stereotypes.md](references/aspect-model-stereotypes.md) for all model identifiers. The test model lives in a Solution module carrying the **`tests` facet**, not in the language module itself, so it can depend on the language under test plus arbitrary runtime libraries.

This skill is the reference for *what to put in a test model* and *what each test type means*. For the mechanics of creating/modifying nodes via MCP, see `mps-model-manipulation` and the `mps_mcp_*` tool docs. Documentation: <https://www.jetbrains.com/help/mps/testing-languages.html>. Source: `plugins/mps-testing/languages/lang.test/`.

## Critical Directives

- The test model **must** carry stereotype `tests` (file name `…@tests.mps`). Without it, roots compile but are not discovered as JUnit tests.
- Used languages on a test model: `jetbrains.mps.lang.test`, `jetbrains.mps.baseLanguage.unitTest`, the language(s) under test, plus `jetbrains.mps.baseLanguage`, `jetbrains.mps.baseLanguage.collections`, `jetbrains.mps.lang.smodel`, `jetbrains.mps.lang.text` as needed by assertion code.
- The containing Solution must carry the **`tests` facet**. Languages and Generators don't compile the test classes, so the test root must live in this dedicated Solution. Create it via `mps_mcp_create_module(type="solution", …, facets="[\"tests\"]")`; the response is self-describing — `{"data":{"kind":"Solution","facets":["java","tests"],"loadExtensions":"NotAvailable",…}}` confirms the kind without a follow-up call. For an existing Solution, attach the facet via `mps_mcp_update_module_facet(facetType="tests", enabled=true)` and verify with `mps_mcp_get_project_structure(startingPoint="…")` (its `facets` array will include `"tests"`).
- The `testMethods` role declares `NodesTestMethod`, which is **abstract** — instantiate `SimpleNodeTest` (`c:8585453e-6bfb-4d80-98de-b16074f1d86c/1225978065297`). Inserting raw `NodesTestMethod` fails with `"Abstract concept instance detected"`.
- Inside snippets, `TestNodeAnnotation` labels are **only** resolvable via `TestNodeReference` from test-method bodies. For *in-snippet* references (e.g. setting `RoutineCall.routine`) use the target node's own `name` property (resolved through the language's scope) or a persistent `r:` node ref, never the label name.
- Prefer `invoke action <ActionId>` over raw `press <chord>` for Enter/Tab/etc. in `EditorTestCase` — `PressKeyStatement` may bypass the named-action dispatcher used by the production editor. Editor actions live in `r:9832fb5f-2578-4b58-8014-a5de79da988e(jetbrains.mps.ide.editor.actions)`.
- For an empty list role, target `empty_<role>` for the caret — *not* `refNodeList_<role>` (the latter is the populated wrapper cell).
- Never edit `test_gen/` or `classes_gen/` — those are regenerated.

## Common-Path Workflow

When asked to "write tests" for an MPS language feature:

1. **Decide the test type.** Typesystem / scope / constraint → `NodesTestCase`. Intention / action / keystroke → `EditorTestCase`. Migration → `MigrationTestCase`. Runtime helper → `BTestCase`. Generator output equivalence → `NodesTestCase` + `assertNodesMatch`. See `references/test-types.md`.
2. **Find or create the test model.** Prefer extending an existing `@tests` model in the language's adjacent test Solution; only create a new one if no suitable Solution exists.
3. **Add the test root** with `mps_mcp_create_root_node` using the concept ref from `references/concept-identifiers.md`, then populate `nodes` / `code` / etc. with `mps_mcp_update_node`. Use `mps_mcp_parse_java_and_insert` to drop a Java/BaseLanguage snippet into a `TestNode`.
4. **Mark assertions** using the appropriate annotation concept (see `references/nodes-test-case.md` and `references/editor-test-case.md`). For prototypes, open the model in MPS and use the Alt+Enter intentions ("Add Node Operations Test Annotation", "Add Test Node Label", "Mark Caret Position") — they create the right annotations without hand-building blueprints.
5. **Validate** with `mps_mcp_check_root_node_problems` on the new root.
6. **Run.** Register a `JUnit Tests` config via `mps_mcp_create_run_configuration` — `modelReference` for the `@tests` model, or one test root in `nodeReference` when iterating on a single test — and launch it with `execute_run_configuration`, or ask the user to run it in MPS.

## Where Test Models Live

| Where | When |
|---|---|
| **Test aspect** of the language (`testAspect` model under `<lang>/`) | Quick, language-local checks; cannot run in a fresh MPS instance from outside the language. |
| **Dedicated test Solution** with `@tests` model | Recommended for anything serious. Runs out-of-process; can be wired into the build/CI. |

If the only goal is plain JUnit on hand-written Java/Kotlin (no MPS-specific assertions), prefer a regular module test source root rather than a `@tests` model — see `mps-distribution-build` for how `BTestCase`-based MPS test modules are packaged.

## Related Skills

- `mps-collections-and-closures` — the collections / closures reference for assertion bodies.
- `mps-model-manipulation` — the smodel reference for assertion bodies; for an assertion body open only `references/dot-expression-basics.md` in the `mps-model-manipulation` skill root after loading that companion skill from the same origin.
- `mps-node-editing` — MCP recipes for inserting children and harvesting persistent refs (used heavily by the `ScopesTest` staged-construction recipe).
- `mps-aspect-typesystem` — when the system-under-test is the typesystem; `has error/type` annotations here verify those rules.
- `mps-aspect-constraints` — when validating `can-be` / scope / validator rules from the constraints aspect.
- `mps-aspect-migrations` — for migration scripts being exercised by `MigrationTestCase`.
- `mps-aspect-intentions` and `mps-aspect-editor-menus-and-keymaps` — when an `EditorTestCase` exercises intentions, side transforms, or keymapped actions.
- `mps-build-language` and `mps-distribution-build` — when wiring a `@tests` Solution into the build pipeline (test-only `BuildProject`).
- `mps-quotations` — for tests that use quotation snippets.

## Reference Index

- Open `references/test-model-setup.md` when bootstrapping a new `@tests` model — required stereotype, used languages, Solution kind, optional `TestInfo` root, and the choice between the language's `testAspect` and a dedicated test Solution. Includes the canonical example test Solutions in this repo.
- Open `references/test-types.md` for the test-type cheat sheet (concept refs and what each tests). Use to decide which root concept to instantiate.
- Open `references/nodes-test-case.md` when authoring or modifying a `NodesTestCase` — section layout (`nodes`, `test methods`, `utility methods`, `accessMode`), inline assertion annotations (`has error`, `has type`, `has typesystem error`, `has expected type`, `has type in`), labelled-node assertions inside test methods, and the namespace separation between labels and scope-resolved references.
- Open `references/scopes-test.md` when writing **declarative** scope assertions (`ScopesTest` + `ScopesExpectedNode`) — where to attach the annotation, the exact-set semantics, the JSON blueprint, the stage-construction recipe for resolving expected refs, and why declarative scope tests beat imperative ones.
- Open `references/editor-test-case.md` when writing an `EditorTestCase` — root layout (`before` / `result` / `code`), the `AnonymousCellAnnotation` caret marker (which properties `before` sets and which the `result` check compares; a `result` caret needs `selectionStart`/`selectionEnd` too), the cell-ID convention for auto-generated editors (`property_<name>`, `refNodeList_<role>`, `empty_<role>`, `refNode_<role>`, constant/collection cells), the editor-driving statements (`type "..."`, `press`, `invoke action`, `invoke intention`, `invoke quick-fix`), the editor expressions usable inside `code` (`EditorComponentExpression` → `getEditorComponent()`, `IsIntentionApplicableExpression` → `isIntentionApplicable()`, `ProjectExpression` → `getProject()`, `ModelExpression` → model of the edited node), and the side-transform / completion testing pattern.
- Open `references/generator-and-migration-tests.md` when validating generator output (`NodesTestCase` + `assertNodesMatch`) or testing a migration script (`MigrationTestCase` — `inputNodes`, `outputNodes`, `migration` refs, `option`s including `StableIdOption`). Lists the four-child structure of `MigrationTestCase` and the "Generate Output from Input" intention.
- Open `references/btestcase.md` when writing plain JUnit-style tests against hand-written Java/Kotlin — the `BTestCase` annotation, `TestMethod` / `BeforeTest` / `AfterTest`, and the available assertion concepts.
- Open `references/running-tests.md` when launching tests — inside MPS (right-click → Run tests), from the command line via the Ant pipeline, or from an agent via `mps_mcp_create_run_configuration` + `execute_run_configuration`. Covers in-process vs separate-MPS-instance, and `mps_mcp_check_root_node_problems` as a fast pre-run gate.
- Open `references/common-failures.md` when a test won't run, runs but doesn't fail when expected, fails for the wrong reason, or generates a misleading `outputNodes`. Includes an `EditorTestCase` symptom → fix table for caret and selection mismatches (`CellReference.java:57/58/61`, typed text replacing a non-empty `before` selection), the abstract-concept gotcha, label-vs-name confusion, `press VK_ENTER` vs `invoke action Insert`, `replace_node_child` ref invalidation, and `ScopesExpectedNode.ref` not being scope-resolved.
- Open `references/concept-identifiers.md` for the validated FQN+concept-ref table covering `jetbrains.mps.lang.test` (every concept used by the skill — `NodesTestCase`, `SimpleNodeTest`, `TestNode`, `TestNodeAnnotation`, `AnonymousCellAnnotation`, the annotation/check operations, the editor-driving statements, `ScopesTest`/`ScopesExpectedNode`, etc.) and `jetbrains.mps.baseLanguage.unitTest`.
