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
13 changes: 0 additions & 13 deletions .claude/settings.local.json

This file was deleted.

253 changes: 253 additions & 0 deletions .claude/skills/constraint-documentation/skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
---
name: NUnit Constraint Documentation
description: Create and update documentation for NUnit constraints following the established template structure
---

# NUnit Constraint Documentation

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

## Documentation Template Structure

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

````markdown
---
uid: constraint-<name>
---

# <Name> Constraint

Brief description (2-3 sentences) explaining what the constraint tests and when to use it.

## Usage

```csharp
Is.<Method>(args) // Primary syntax
Does.<Method>(args) // If applicable
Has.<Method>(args) // If applicable
```

## Modifiers

```csharp
.IgnoreCase // Makes comparison case-insensitive
.Using(IComparer comparer) // Uses custom comparison logic
.Within(tolerance) // Allows approximate matching
```

> [!NOTE]
> Only include this section if the constraint has modifiers.

## Examples

Inline one-liners showing common patterns:

```csharp
Assert.That(actual, Is.<Method>(expected));
Assert.That(actual, Is.Not.<Method>(expected));
Assert.That(actual, Is.<Method>(expected).IgnoreCase);
```

### [Scenario Section - only if complex setup needed]

Brief explanation of the scenario.

[!code-csharp[ScenarioExample](~/snippets/Snippets.NUnit/ConstraintExamples.cs#ScenarioExample)]

## Notes

1. Important behavioral notes
2. Edge cases
3. Compatibility information

## See Also

* [Related Constraint](related.md)
* [Another Related](another.md)
````

## Key Structural Principles

| Section | Purpose |
|---------|---------|
| Usage (first) | Users interact with fluent API (`Is.*`, `Does.*`), not constructors |
| Modifiers | Use `.ModifierName` format (shows method chaining) |
| Examples | Inline one-liners for common patterns |
| Notes | Edge cases and important behaviors |
| See Also | Links to related constraints |

## Inline vs External Examples

**Inline examples** (in the markdown itself):
- **Usage section**: Always inline - shows the API surface (1-3 lines of just the syntax)
- **Examples section**: One-liners demonstrating common patterns without test boilerplate

````markdown
## Usage

```csharp
Is.Null
Is.Not.Null
```

## Examples

```csharp
Assert.That(myObject, Is.Null);
Assert.That(myString, Is.Not.Null);
Assert.That(GetResult(), Is.Null.Or.Empty); // Combined with other constraints
```
````

**External snippets** (from Snippets.NUnit):
- Complex scenarios requiring setup code (custom comparers, test data, helper classes)
- Full compilable test methods that verify the documentation is correct
- Scenarios where the setup/context is important to understand

````markdown
### Using Custom Comparers

[!code-csharp[EqualWithComparer](~/snippets/Snippets.NUnit/ConstraintExamples.cs#EqualWithComparer)]
````

**Never inline:**
- Full `[Test] public void...` methods - those go in external snippets
- Examples requiring helper classes or significant setup

## UID Standardization

All constraint UIDs should follow the pattern: `constraint-<lowercase-name>`

Examples:
- `equalconstraint` → `constraint-equal`
- `nullconstraint` → `constraint-null`
- `greaterthanconstraint` → `constraint-greaterthan`

## Workflow for Updating Constraint 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 constraint class in the NUnit framework:

```shell
# Find the constraint source file (adjust path to your nunit repo location)
grep -r "class <Name>Constraint" ../nunit/src/NUnitFramework/framework/Constraints --include="*.cs"
```

The source is typically at:
`<nunit-repo>/src/NUnitFramework/framework/Constraints/<Name>Constraint.cs`

### Step 2: Analyze the Source Code

From the source file, extract:
- Available syntax helpers (check `Is.cs`, `Does.cs`, `Has.cs`, `Contains.cs`)
- Modifier methods and their purposes
- Constructor parameters (for reference, not for documentation focus)
- Any special behaviors or edge cases from XML doc comments

### Step 3: Check for Existing Unit Tests

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

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

### Step 4: Update the Markdown Documentation

Follow the template structure above. Key points:
- Lead with Usage section showing the fluent API
- Include inline examples for quick reference
- Add external snippet references for complex scenarios
- Document all available modifiers with brief descriptions

### Step 5: Update or Create Snippet Examples

If complex scenarios need external snippets, add them to:
`docs/snippets/Snippets.NUnit/ConstraintExamples.cs`

Use regions to organize:

```csharp
#region <Name>ConstraintExamples
[Test]
public void <Name>Constraint_WithCustomComparer()
{
// Example with setup that's too complex for inline
var comparer = new MyCustomComparer();
Assert.That(actual, Is.<Method>(expected).Using(comparer));
}
#endregion
```

### 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 constraint tests
dotnet test docs/snippets/Snippets.slnx --no-build --filter "FullyQualifiedName~ConstraintExamples" --verbosity normal
```

## Quality Checklist

Before marking a constraint as done:

- [ ] UID follows `constraint-<name>` pattern
- [ ] Description is 2-3 sentences explaining purpose
- [ ] Usage section shows fluent API methods (inline, 1-3 lines)
- [ ] Modifiers section (if applicable) with brief descriptions
- [ ] Examples section with inline one-liners for common patterns
- [ ] External snippets for complex scenarios (custom comparers, setup-heavy)
- [ ] Examples have subsection headers if multiple scenarios exist
- [ ] Notes section for edge cases/important behaviors
- [ ] See Also links to related constraints
- [ ] Version info for features added after NUnit 3.0

## Common Constraint Patterns

### Simple Constraints (no modifiers)
Examples: `NullConstraint`, `TrueConstraint`, `FalseConstraint`, `NaNConstraint`

These need minimal documentation but should still have:
- Clear description
- Usage showing `Is.<Name>` and `Is.Not.<Name>`
- 2-3 inline examples

### Comparison Constraints (with Using/Within modifiers)
Examples: `EqualConstraint`, `GreaterThanConstraint`, `LessThanConstraint`

These need:
- Modifiers section documenting `.Using()` and `.Within()` variants
- Examples showing basic comparison and modifier usage
- External snippets for custom comparer scenarios

### Collection Constraints (with collection-specific modifiers)
Examples: `CollectionEquivalentConstraint`, `UniqueItemsConstraint`, `AllItemsConstraint`

These need:
- Modifiers section with `.IgnoreCase`, `.Using()`, `.UsingPropertiesComparer()`
- Examples showing different collection types
- Notes about element comparison behavior

### String Constraints (with string-specific modifiers)
Examples: `StartsWithConstraint`, `SubstringConstraint`, `RegexConstraint`

These need:
- Modifiers for `.IgnoreCase`, `.Using(StringComparison)`, `.Using(CultureInfo)`
- Examples showing case-sensitive and case-insensitive usage
- Notes about culture-specific behavior where applicable
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
code-output/
code-output/
**/.claude/settings.local.json
CLAUDE.local.md
**/.claude/history/
.claude/**/*.log

Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,5 @@ NUnit 3.0 adds the ability to compare generic collections and dictionaries.

## See Also

* [Equal Constraint](xref:equalconstraint)
* [Equal Constraint](xref:constraint-equal)
* [DefaultFloatingPointTolerance Attribute](../../attributes/defaultfloatingpointtolerance.md)
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ See [Assert.AreEqual](Assert.AreEqual.md) for details of how NUnit performs equa
## See Also

* [Assert.AreEqual](Assert.AreEqual.md)
* [Equal Constraint](xref:equalconstraint)
* [Equal Constraint](xref:constraint-equal)
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ Assert.AreNotSame(object expected, object actual,

## See Also

* [SameAs Constraint](xref:sameasconstraint)
* [SameAs Constraint](xref:constraint-sameas)
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ Assert.AreSame(object expected, object actual,

## See Also

* [SameAs Constraint](xref:sameasconstraint)
* [SameAs Constraint](xref:constraint-sameas)
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ T Assert.Catch<T>(TestDelegate code,
* [Assert.CatchAsync](Assert.CatchAsync.md)
* [Assert.Throws](Assert.Throws.md)
* [Assert.ThrowsAsync](Assert.ThrowsAsync.md)
* [ThrowsConstraint](xref:throwsconstraint)
* [ThrowsConstraint](xref:constraint-throws)
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ T Assert.CatchAsync<T>(AsyncTestDelegate code,
* [Assert.Catch](Assert.Catch.md)
* [Assert.Throws](Assert.Throws.md)
* [Assert.ThrowsAsync](Assert.ThrowsAsync.md)
* [ThrowsConstraint](xref:throwsconstraint)
* [ThrowsConstraint](xref:constraint-throws)
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ void Assert.DoesNotThrow(TestDelegate code,
## See Also

* [Assert.Throws](Assert.Throws.md)
* [ThrowsConstraint](xref:throwsconstraint)
* [ThrowsConstraint](xref:constraint-throws)
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ void Assert.DoesNotThrowAsync(AsyncTestDelegate code,
## See Also

* [Assert.ThrowsAsync](Assert.ThrowsAsync.md)
* [ThrowsConstraint](xref:throwsconstraint)
* [ThrowsConstraint](xref:constraint-throws)
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,4 @@ Assert.Catch(code);
* [Assert.Catch](Assert.Catch.md)
* [Assert.CatchAsync](Assert.CatchAsync.md)
* [Assert.ThrowsAsync](Assert.ThrowsAsync.md)
* [ThrowsConstraint](xref:throwsconstraint)
* [ThrowsConstraint](xref:constraint-throws)
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ additional verification of the exception. Note that you do not need to await the
* [Assert.Catch](Assert.Catch.md)
* [Assert.CatchAsync](Assert.CatchAsync.md)
* [Assert.Throws](Assert.Throws.md)
* [ThrowsConstraint](xref:throwsconstraint)
* [ThrowsConstraint](xref:constraint-throws)
39 changes: 21 additions & 18 deletions docs/articles/nunit/writing-tests/constraints/AllItemsConstraint.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
---
uid: constraint-allitems
---

# AllItems Constraint

`AllItemsConstraint` applies a constraint to each item in an `IEnumerable`, succeeding only if all of them succeed. An
exception is thrown if the actual value passed does not implement `IEnumerable`.
`AllItemsConstraint` applies a constraint to each item in an `IEnumerable`, succeeding only if all items satisfy the
constraint. An exception is thrown if the actual value does not implement `IEnumerable`.

## Constructor
## Usage

```csharp
AllItemsConstraint(Constraint itemConstraint)
Is.All.<constraint>
Has.All.<constraint>
```

## Syntax
## Examples

```csharp
Is.All...
Has.All...
```
[!code-csharp[AllItemsConstraintExamples](~/snippets/Snippets.NUnit/Constraints/CollectionConstraintSnippets.cs#AllItemsConstraintExamples)]

## Examples of Use
## Notes

```csharp
int[] iarray = new int[] { 1, 2, 3 };
string[] sarray = new string[] { "a", "b", "c" };
Assert.That(iarray, Is.All.Not.Null);
Assert.That(sarray, Is.All.InstanceOf<string>());
Assert.That(iarray, Is.All.GreaterThan(0));
Assert.That(iarray, Has.All.GreaterThan(0));
```
1. `Is.All` and `Has.All` are interchangeable.
2. The constraint fails on the first item that doesn't match.
3. An empty collection satisfies `Is.All` for any constraint (vacuous truth).

## See Also

* [SomeItems Constraint](SomeItemsConstraint.md) - At least one item matches
* [NoItem Constraint](NoItemConstraint.md) - No items match
* [ExactCount Constraint](ExactCountConstraint.md) - Specific number of items match
Loading
Loading