Skip to content

Commit e501a4b

Browse files
aviditCopilot
andauthored
DYN-10274 Add skill - dynamo-unit-testing (#16960)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 2d7aebc commit e501a4b

9 files changed

Lines changed: 621 additions & 51 deletions

File tree

.agents/README.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Each skill lives in its own folder with a `SKILL.md` and optionally a `template.
5959
| [dynamo-pr-description](skills/dynamo-pr-description/SKILL.md) | Writing PR descriptions matching the Dynamo template. | Repo-specific variant |
6060
| [dynamo-jira-ticket](skills/dynamo-jira-ticket/SKILL.md) | Creating or refining Jira tickets from bugs, test failures, or feature requests. | Repo-specific variant |
6161
| [dynamo-skill-writer](skills/dynamo-skill-writer/SKILL.md) | Authoring/updating skills and enforcing sync across Copilot, Cursor, and Claude surfaces. | Repo-specific variant |
62+
| [dynamo-unit-testing](skills/dynamo-unit-testing/SKILL.md) | Writing NUnit tests following Dynamo patterns. Test classes, setup/teardown, .dyn file testing. | Repo-specific variant |
6263

6364
### Templates (bundled with skills)
6465

@@ -67,13 +68,13 @@ Templates are co-located inside the skill folder that uses them:
6768
| Template | Location | Purpose |
6869
|----------|----------|---------|
6970
| PR description | [.github/PULL_REQUEST_TEMPLATE.md](../../.github/PULL_REQUEST_TEMPLATE.md) | GitHub PR template (referenced by PR description skill) |
70-
| Jira triage | [dynamo-jira-ticket/template.md](skills/dynamo-jira-ticket/template.md) | Triage a Jira ticket into a structured issue |
71+
| Jira triage | [dynamo-jira-ticket/template.md](./skills/dynamo-jira-ticket/template.md) | Triage a Jira ticket into a structured issue |
7172

7273
### Rules (short guardrails -- always applicable)
7374

7475
| Rule | Scope | Repo scope |
7576
|------|-------|------------|
76-
| [dynamo-core-rules](rules/dynamo-core-rules.md) | C#/.NET coding standards, NUnit, PublicAPI, security, quality checks. | Repo-specific variant |
77+
| [dynamo-core-rules](./rules/dynamo-core-rules.md) | C#/.NET coding standards, NUnit, PublicAPI, security, quality checks. | Repo-specific variant |
7778

7879
## Folder Structure
7980

@@ -87,10 +88,14 @@ Templates are co-located inside the skill folder that uses them:
8788
│ ├── dynamo-pr-description/
8889
│ │ ├── SKILL.md ← workflow for writing PR descriptions
8990
│ ├── dynamo-jira-ticket/
90-
│ ├── SKILL.md ← workflow for writing Jira tickets
91-
│ └── template.md ← copy/paste Jira template
92-
│ └── dynamo-skill-writer/
93-
│ └── SKILL.md ← workflow for authoring and syncing skills
91+
│ │ ├── SKILL.md ← workflow for writing Jira tickets
92+
│ │ └── template.md ← copy/paste Jira template
93+
│ ├── dynamo-skill-writer/
94+
│ │ └── SKILL.md ← workflow for authoring and syncing skills
95+
│ └── dynamo-unit-testing/
96+
│ ├── SKILL.md ← workflow for writing tests
97+
│ ├── test-patterns.md ← code templates & examples
98+
│ └── quality-checklist.md ← guidelines & best practices
9499
├── rules/
95100
│ └── dynamo-core-rules.md
96101
└── README.md ← you are here
@@ -108,7 +113,8 @@ Templates are co-located inside the skill folder that uses them:
108113
.cursor/skills/ <-- Cursor skill mirror/pointers
109114
.github/copilot-instructions.md <-- Copilot guidance + pointers here
110115
.github/agents/ <-- existing Copilot agents (Janitor, UX Designer, ContentDesigner)
111-
CLAUDE.md <-- Claude guidance + pointers here
116+
../AGENTS.md <-- AI Agents guidance overview + pointers here
117+
../CLAUDE.md <-- Claude guidance + pointers here
112118
```
113119

114120
## For Cursor users
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
name: dynamo-unit-testing
3+
description: Write comprehensive NUnit tests for the Dynamo codebase following Dynamo testing patterns, conventions, and architectural constraints. Use this skill when writing test methods, test classes, setting up test infrastructure, mocking dependencies, testing with .dyn files, or reviewing test code in Dynamo.
4+
---
5+
6+
# Dynamo Unit Testing
7+
8+
## When to use
9+
- Writing new test methods or test classes for Dynamo components
10+
- Setting up test infrastructure (base classes, setup/teardown)
11+
- Testing nodes, workspaces, or UI components
12+
- Working with .dyn test files or sample graphs
13+
- Reviewing existing tests for quality and patterns
14+
- Debugging test failures or flaky tests
15+
16+
## When not to use
17+
- General coding questions -- use [dynamo-dotnet-expert](../dynamo-dotnet-expert/SKILL.md) instead
18+
- Architecture questions -- use [dynamo-onboarding](../dynamo-onboarding/SKILL.md) instead
19+
- PR descriptions -- use [dynamo-pr-description](../dynamo-pr-description/SKILL.md) instead
20+
21+
## Inputs expected
22+
Test requirements, failing functionality to test, existing code to cover, or test code to review.
23+
24+
## Output format
25+
Complete test classes with proper setup, test methods using Dynamo patterns, and explanations of testing approach.
26+
27+
---
28+
29+
## Workflow
30+
31+
> 🚀 **Quick Start**: [test-patterns.md](test-patterns.md) has ready-to-use templates
32+
33+
### 1. Choose Your Base Class
34+
Select the appropriate test base class based on what you're testing:
35+
- **UnitTestBase** - Utilities, algorithms, no Dynamo model needed
36+
- **DynamoModelTestBase** - Nodes, workspaces, commands (most common)
37+
- **DSEvaluationUnitTestBase** - DesignScript expression evaluation
38+
39+
### 2. Apply Testing Standards
40+
Follow NUnit conventions and Dynamo patterns. See [quality-checklist.md](quality-checklist.md) for guidelines and anti-patterns and Dynamo coding standards for general coding practices.
41+
- [Dynamo Coding Standards](https://github.com/DynamoDS/Dynamo/wiki/Coding-Standards)
42+
- [Dynamo Naming Standards](https://github.com/DynamoDS/Dynamo/wiki/Naming-Standards)
43+
44+
### 3. Write Focused Tests
45+
One behavior per test, descriptive names, Arrange-Act-Assert structure.
46+
47+
## Testing Principles
48+
49+
- **NUnit only** - Use NUnit framework exclusively (never xUnit/MSTest)
50+
- **One behavior per test** - Each test validates exactly one scenario
51+
- **Pragmatic .dyn testing** - .dyn file tests allowed in unit categories for focused functionality
52+
53+
## Documentation
54+
55+
**Assets & References:**
56+
- **[test-patterns.md](test-patterns.md)** - Base classes, code templates, common scenarios, complete examples
57+
- **[quality-checklist.md](quality-checklist.md)** - Quality guidelines, anti-patterns, best practices
58+
59+
**Related Skills:**
60+
[dynamo-dotnet-expert](../dynamo-dotnet-expert/SKILL.md)[dynamo-onboarding](../dynamo-onboarding/SKILL.md)
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# Test Quality Guidelines & Anti-Patterns
2+
3+
## Test Quality Checklist
4+
5+
-**One behavior per test** - Each test validates exactly one scenario
6+
-**Descriptive names** - Test name clearly describes the scenario
7+
-**Arrange-Act-Assert** - Clear separation of test phases
8+
-**Independent tests** - Tests don't depend on each other's state
9+
-**Proper cleanup** - Resources are disposed, temp files removed
10+
-**Meaningful assertions** - Assertions validate the important behavior
11+
-**No hardcoded paths** - Use TestDirectory, SampleDirectory, TempFolder
12+
-**Category tags** - Apply appropriate [Category] attributes
13+
14+
## Common Anti-Patterns to Avoid
15+
16+
### ❌ Multiple behaviors in one test
17+
```csharp
18+
// DON'T: Testing multiple unrelated things
19+
[Test]
20+
public void TestEverything()
21+
{
22+
// Tests creation, execution, and saving all in one test
23+
}
24+
```
25+
26+
### ✅ Split into focused tests
27+
```csharp
28+
[Test] public void NodeCreatedWithCorrectProperties() { }
29+
[Test] public void NodeExecutesAndProducesExpectedOutput() { }
30+
[Test] public void WorkspaceSavesWithCorrectStructure() { }
31+
```
32+
33+
### ❌ Ignored tests without explanation
34+
```csharp
35+
[Test]
36+
[Ignore] // DON'T: No explanation why
37+
public void SomeTest() { }
38+
```
39+
40+
### ✅ Documented ignored tests
41+
```csharp
42+
[Test]
43+
[Ignore("WIP - Node execution logic being refactored in DYN-1234")]
44+
public void NewNodeExecutesCorrectly() { }
45+
```
46+
47+
### ❌ Empty catch blocks
48+
```csharp
49+
try
50+
{
51+
RiskyOperation();
52+
}
53+
catch
54+
{
55+
// DON'T: Silent failure
56+
}
57+
```
58+
59+
### ✅ Proper error handling
60+
```csharp
61+
try
62+
{
63+
RiskyOperation();
64+
}
65+
catch (ExpectedException ex)
66+
{
67+
// Log the error for debugging
68+
Console.WriteLine($"Expected error: {ex.Message}");
69+
// Re-throw or handle appropriately
70+
throw;
71+
}
72+
```
73+
74+
### ❌ Weak assertions
75+
```csharp
76+
// DON'T: Weakened assertions
77+
Assert.IsNotNull(result); // When you could be more specific
78+
79+
// DO: Specific assertions
80+
Assert.AreEqual(expectedValue, result);
81+
Assert.AreEqual(3, collection.Count());
82+
```
83+
84+
### ❌ Tests with side effects
85+
```csharp
86+
// DON'T: Tests affecting global state
87+
[Test]
88+
public void TestThatChangesGlobalConfig()
89+
{
90+
GlobalConfig.Setting = "test value"; // Affects other tests
91+
// ... test logic
92+
// No cleanup
93+
}
94+
```
95+
96+
### ✅ Isolated tests with cleanup
97+
```csharp
98+
[Test]
99+
public void TestWithProperIsolation()
100+
{
101+
// Arrange
102+
var originalValue = GlobalConfig.Setting;
103+
104+
try
105+
{
106+
// Act
107+
GlobalConfig.Setting = "test value";
108+
// ... test logic
109+
}
110+
finally
111+
{
112+
// Cleanup
113+
GlobalConfig.Setting = originalValue;
114+
}
115+
}
116+
```
117+
118+
## File Organization Guidelines
119+
120+
### Test File Structure
121+
```
122+
test/
123+
├── DynamoCoreTests/ # Core engine tests
124+
│ ├── Graph/
125+
│ ├── Nodes/
126+
│ └── Utilities/
127+
├── DynamoCoreWpfTests/ # UI component tests
128+
├── Libraries/ # Per-library tests
129+
│ ├── CoreNodesTests/
130+
│ └── PythonNodeModelsTests/
131+
└── System/ # Integration tests
132+
```
133+
134+
### Test Categories
135+
- `[Category("UnitTests")]` - Unit tests (including focused .dyn file tests)
136+
- `[Category("RegressionTests")]` - Tests for specific bug fixes
137+
- `[Category("Failure")]` - Tests that demonstrate known failures
138+
139+
**Note**: Dynamo uses a pragmatic approach where tests loading .dyn files can be categorized as unit tests when they focus on testing specific functionality rather than broad integration scenarios.
140+
141+
## Naming Conventions
142+
143+
### Preferred Naming Styles
144+
145+
1. **Descriptive approach** (when scenario is clear):
146+
```csharp
147+
HomeWorkspaceCanSaveAsNewFile()
148+
CustomNodeFunctionalityWorksCorrectly()
149+
```
150+
151+
2. **When/Then format** (for complex scenarios):
152+
```csharp
153+
WhenNodeIsCreatedThenItHasCorrectProperties()
154+
WhenInvalidInputThenThrowsException()
155+
```
156+
157+
### Naming Guidelines
158+
- Use present tense for actions
159+
- Be specific about the scenario being tested
160+
- Include the expected outcome when it's not obvious
161+
- Avoid generic names like `Test1()` or `TestMethod()`

0 commit comments

Comments
 (0)