Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 244 additions & 0 deletions .claude/skills/attribute-documentation/skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
---
name: NUnit Attribute Documentation
description: Create and update documentation for NUnit attributes following the established template structure
---

# NUnit Attribute Documentation

This skill describes how to create and update documentation for NUnit attributes.

## Documentation Template Structure

Each attribute documentation file in `docs/articles/nunit/writing-tests/attributes/` should follow this structure:

````markdown
Comment thread
OsirisTerje marked this conversation as resolved.
# AttributeName

Brief description of what the attribute does.

## Constructor

```csharp
AttributeName(paramType paramName)
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `paramName` | `Type` | Description of the parameter. Include important notes like valid ranges or default behavior. |

## Properties

| Property | Type | Description | Default |
|----------|------|-------------|---------|
| `PropertyName` | `Type` | Description of the property. | `defaultValue` |

> [!NOTE]
> Version notes if applicable (e.g., "Added in **NUnit 4.5.0**").

## Applies To

Use a table with checkmarks to show where the attribute can be applied:

| Target | Supported |
|--------|-----------|
| Test Methods | ✅ |
| Test Fixtures (Classes) | ✅ |
| Assemblies | ❌ |

Use ✅ (green checkmark) where the attribute applies, ❌ (red X) where it doesn't.

## Example

[!code-csharp[ExampleName](~/snippets/Snippets.NUnit/Attributes/AttributeNameExamples.cs#RegionName)]

## Notes

1. Important behavioral notes
2. Limitations
3. Edge cases

## See Also

* [Related Attribute](relatedattribute.md)
````

### For Parameterless Attributes

Use a simpler "Usage" section instead of Constructor/Properties:

````markdown
## Usage

This is a parameterless attribute that can only be applied to [target].

```csharp
[AttributeName]
```
````

## Workflow for Updating Attribute Documentation

> **Repository Layout Assumption**: This workflow assumes you have cloned both repositories as siblings:
> - `<workspace>/nunit/` - The NUnit framework source code ([nunit/nunit](https://github.com/nunit/nunit))
> - `<workspace>/docs/` - This documentation repository ([nunit/docs](https://github.com/nunit/docs))
>
> Adjust paths below to match your local setup.

### Step 1: Find the Source Code

Search for the attribute class in the NUnit framework:

```shell
# Find the attribute source file (adjust path to your nunit repo location)
grep -r "class AttributeNameAttribute" ../nunit --include="*.cs"
```

The source is typically at:
`<nunit-repo>/src/NUnitFramework/framework/Attributes/AttributeNameAttribute.cs`

### Step 2: Analyze the Source Code

From the source file, extract:
- Constructor parameters and their XML doc comments
- Properties with their types, descriptions, and default values
- `AttributeUsage` to determine valid targets (Class, Method, Assembly)
- Whether `Inherited = true`

### Step 3: Check for Existing Unit Tests

```shell
# Find unit tests for the attribute (adjust path to your nunit repo location)
grep -r "AttributeName" ../nunit/src/NUnitFramework/tests --include="*.cs"
```

Tests are typically at:
`<nunit-repo>/src/NUnitFramework/tests/Attributes/AttributeNameAttributeTests.cs`

Unit tests provide good examples of usage patterns and edge cases.

### Step 4: Create Snippet File

Create a new file at:
`docs/snippets/Snippets.NUnit/Attributes/AttributeNameAttributeExamples.cs`

Follow this pattern:

```csharp
using NUnit.Framework;

namespace Snippets.NUnit.Attributes
{
public class AttributeNameAttributeExamples
{
#region ExampleName
[TestFixture]
public class ExampleTests
{
[Test]
public void ExampleTest()
{
// Test code that demonstrates the attribute
Assert.That(true, Is.True);
}
}
#endregion

#region AnotherExample
// More examples...
#endregion
}
}
```

Key points:
- Use `#region` / `#endregion` to mark code sections
- Region names are used in the markdown reference: `#RegionName`
- Tests must actually pass - avoid stub code that doesn't work
- If you need helper classes, make them private nested classes

### Step 5: Update the Markdown Documentation

Reference snippets using DocFX syntax:

```markdown
[!code-csharp[ExampleName](~/snippets/Snippets.NUnit/Attributes/AttributeNameExamples.cs#ExampleName)]
```

### Step 6: Build and Test

Always verify the snippets compile and tests pass. From the docs repository root:

```shell
# Build
dotnet build docs/snippets/Snippets.slnx

# Run all tests
dotnet test docs/snippets/Snippets.slnx --no-build

# Run specific tests
dotnet test docs/snippets/Snippets.slnx --no-build --filter "FullyQualifiedName~AttributeNameExamples" --verbosity normal
```

### Step 7: Update the Plan File

Mark the attribute as done in `docs-improvement-plan.md`:

```markdown
| AttributeName | Done | Brief notes about what was added |
```

## Common Attribute Patterns

### Attributes with Constructor Parameters Only
Examples: `Retry`, `Repeat`, `MaxTime`, `Order`

### Attributes with Properties
Examples: `Retry` (has `RetryExceptions`), `TestCase` (has many named parameters)

### Parameterless Attributes
Examples: `SingleThreaded`, `NonParallelizable`, `Combinatorial`

### Enum-based Attributes
Examples: `Parallelizable` (takes `ParallelScope`), `Apartment` (takes `ApartmentState`)

Document the enum values in a table.

## Command Line Documentation Guidelines

When documenting command line usage:

1. **Use appropriate code fences** - Use ` ```shell ` for command line examples, not ` ```bash `, ` ```text `, or untagged code blocks.

2. **Prioritize `dotnet test`** - Always mention `dotnet test` before the NUnit console runner, as it is used far more frequently.

3. **Include runsettings equivalents** - When mentioning console runner options (like `--timeout`, `--workers`, etc.), note that these settings can also be configured for `dotnet test` via the NUnit adapter's `.runsettings` file.

Example:

````markdown
## Command Line Usage

```shell
# dotnet test (via NUnit adapter)
dotnet test --filter "TestCategory=Fast"

# Or via runsettings
dotnet test --settings test.runsettings

# NUnit Console Runner
nunit3-console MyTests.dll --where "cat == Fast"
```
````

## Quality Checklist

Before marking an attribute as done:

- [ ] Constructor section with parameter table (if applicable)
- [ ] Properties section with defaults (if applicable)
- [ ] At least one working code example in snippets folder
- [ ] Examples build without errors
- [ ] Example tests pass
- [ ] Notes section with important behavioral info
- [ ] See Also section with related attributes
- [ ] Plan file updated
1 change: 1 addition & 0 deletions cSpell.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"Parallelizable",
"parallelization",
"paramref",
"parameterless",
"PDBs",
"Pluggable",
"prefilter",
Expand Down
2 changes: 1 addition & 1 deletion docs/articles/nunit-analyzers/NUnit1008.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ In `AssemblyInfo.cs`:

### Explanation

`ParallelScope.Self` [only applies to classes and methods](xref:parallelizableattribute),
`ParallelScope.Self` [only applies to classes and methods](xref:attribute-parallelizable),
not to assemblies.

### Fix
Expand Down
2 changes: 1 addition & 1 deletion docs/articles/nunit-analyzers/NUnit1010.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void NUnit1010SampleTest()
In the sample above, `ParallelScope.Fixtures` is specified.

However, in the context of a test method, a scope of `Fixtures` does not make sense. This scope [only applies at the
assembly or class level](xref:parallelizableattribute).
assembly or class level](xref:attribute-parallelizable).

### Fix

Expand Down
2 changes: 1 addition & 1 deletion docs/articles/nunit-analyzers/NUnit1015.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class DivideCases
In the sample above, the class `DivideCases` does not implement `IEnumerable` nor `IAsyncEnumerable`

However, source types specified by `TestCaseSource`
[must implement `IEnumerable` or `IAsyncEnumerable`](xref:testcasesourceattribute).
[must implement `IEnumerable` or `IAsyncEnumerable`](xref:attribute-testcasesource).

### Fix

Expand Down
2 changes: 1 addition & 1 deletion docs/articles/nunit-analyzers/NUnit1016.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class DivideCases : IEnumerable
In the sample above, the class `DivideCases` does not have a default constructor - i.e. a constructor with no parameters.

However, source types specified by `TestCaseSource`
[must have a default constructor](xref:testcasesourceattribute).
[must have a default constructor](xref:attribute-testcasesource).

### Fix

Expand Down
2 changes: 1 addition & 1 deletion docs/articles/nunit-analyzers/NUnit1017.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class MyTestClass
In the sample above, `DivideCases` is not a `static` field.

However, sources specified by `TestCaseSource`
[must be `static`](xref:testcasesourceattribute).
[must be `static`](xref:attribute-testcasesource).

### Fix

Expand Down
2 changes: 1 addition & 1 deletion docs/articles/nunit-analyzers/NUnit1019.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ In the sample above, the source specified by `TestCaseSource` - the field `testC
`I(Async)Enumerable` or a type that implements `I(Async)Enumerable`, instead it returns an `int`.

However, sources specified by `TestCaseSource`
[must return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`.](xref:testcasesourceattribute).
[must return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`.](xref:attribute-testcasesource).

### Fix

Expand Down
2 changes: 1 addition & 1 deletion docs/articles/nunit-analyzers/NUnit1022.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class MyTestClass

In the sample above, `Numbers` is not a `static` property.

However, sources specified by `ValueSource` [must be `static`](xref:valuesource).
However, sources specified by `ValueSource` [must be `static`](xref:attribute-valuesource).

### Fix

Expand Down
2 changes: 1 addition & 1 deletion docs/articles/nunit-analyzers/NUnit1024.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ In the sample above, the source specified by `ValueSource` - the field `testCase
`I(Async)Enumerable` or a type that implements `I(Async)Enumerable`, instead it returns an `int`.

However, sources specified by `ValueSource`
[must return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`.](xref:valuesource).
[must return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`.](xref:attribute-valuesource).

### Fix

Expand Down
2 changes: 1 addition & 1 deletion docs/articles/nunit/Towards-NUnit4.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ the classic .NET Framework, it prevents `finally` clauses and other cleanup rout

Cancellation is supposed to be done in a cooperative way.

To achieve this in NUnit v4, we introduce a [CancelAfter Attribute](./writing-tests/attributes/cancelafter.md)
To achieve this in NUnit v4, we introduce a [CancelAfter Attribute](xref:attribute-cancelafter)

## Nullability

Expand Down
Loading
Loading