|
| 1 | +# Copilot Instructions |
| 2 | + |
| 3 | +## Test Project Conventions |
| 4 | + |
| 5 | +### Unit Test Style |
| 6 | + |
| 7 | +When writing or modifying unit tests in this project: |
| 8 | + |
| 9 | +- **Do NOT use** `// Arrange`, `// Act`, `// Assert` comments in test methods |
| 10 | +- Use blank lines to separate the logical sections of a test instead of comments |
| 11 | +- Keep tests concise and readable without AAA comment annotations |
| 12 | + |
| 13 | +### Example |
| 14 | + |
| 15 | +```csharp |
| 16 | +[Fact] |
| 17 | +public void MyMethod_WhenCondition_ExpectedBehavior() |
| 18 | +{ |
| 19 | + var input = "test"; |
| 20 | + |
| 21 | + var result = MyMethod(input); |
| 22 | + |
| 23 | + Assert.Equal("expected", result); |
| 24 | +} |
| 25 | +``` |
| 26 | + |
| 27 | +## Code Style Conventions |
| 28 | + |
| 29 | +### Collection Expressions |
| 30 | + |
| 31 | +Use C# 12+ collection expressions instead of traditional array/collection initialization: |
| 32 | + |
| 33 | +```csharp |
| 34 | +// Preferred |
| 35 | +byte[] data = [1, 2, 3, 4, 5]; |
| 36 | +List<string> names = ["Alice", "Bob"]; |
| 37 | +int[] empty = []; |
| 38 | + |
| 39 | +// Avoid |
| 40 | +var data = new byte[] { 1, 2, 3, 4, 5 }; |
| 41 | +var names = new List<string> { "Alice", "Bob" }; |
| 42 | +var empty = Array.Empty<int>(); |
| 43 | +``` |
| 44 | + |
| 45 | +### Constants for Literals |
| 46 | + |
| 47 | +Use `const` for any literal values of any type instead of `var`: |
| 48 | + |
| 49 | +```csharp |
| 50 | +// Preferred |
| 51 | +const string expected = "hello"; |
| 52 | +const int count = 5; |
| 53 | +const char separator = ','; |
| 54 | +const double rate = 0.15; |
| 55 | + |
| 56 | +// Avoid |
| 57 | +var expected = "hello"; |
| 58 | +var count = 5; |
| 59 | +var separator = ','; |
| 60 | +var rate = 0.15; |
| 61 | +``` |
| 62 | + |
| 63 | +### UTF-8 String Literals |
| 64 | + |
| 65 | +Use C# 11+ UTF-8 string literals (`u8` suffix) when working with byte arrays that represent valid UTF-8 text: |
| 66 | + |
| 67 | +```csharp |
| 68 | +// Preferred |
| 69 | +var bytes = "Hello"u8.ToArray(); |
| 70 | +var utf8Bytes = "café"u8.ToArray(); |
| 71 | +var emojiBytes = "😀"u8.ToArray(); |
| 72 | + |
| 73 | +// Avoid |
| 74 | +byte[] bytes = [72, 101, 108, 108, 111]; |
| 75 | +byte[] utf8Bytes = [0xC3, 0xA9]; // 'é' in UTF-8 |
| 76 | +byte[] emojiBytes = [0xF0, 0x9F, 0x98, 0x80]; // 😀 in UTF-8 |
| 77 | +``` |
| 78 | + |
| 79 | +Note: Use explicit byte arrays for invalid UTF-8 sequences or when testing error conditions. |
| 80 | + |
| 81 | +## Code Quality |
| 82 | + |
| 83 | +### ReSharper/IDE Warnings |
| 84 | + |
| 85 | +Always fix ReSharper and IDE warnings/suggestions when modifying code. Common fixes include: |
| 86 | + |
| 87 | +- Use collection expressions where applicable (but cast when needed for generic type inference, e.g., `(byte[])[1, 2, 3]` for `ReadOnlyMemory<T>` parameters) |
| 88 | +- Remove redundant type specifications |
| 89 | +- Use `const` for compile-time constant values |
| 90 | +- Ensure all record struct properties are accessed to avoid "never accessed" warnings |
| 91 | + |
| 92 | +### xUnit Assertion Best Practices |
| 93 | + |
| 94 | +Use the appropriate xUnit assertions for collection sizes instead of `Assert.Equal`: |
| 95 | + |
| 96 | +```csharp |
| 97 | +// Preferred |
| 98 | +Assert.Empty(collection); // For count == 0 |
| 99 | +Assert.Single(collection); // For count == 1 |
| 100 | +
|
| 101 | +// Avoid |
| 102 | +Assert.Equal(0, collection.Count); |
| 103 | +Assert.Equal(1, collection.Count); |
| 104 | +``` |
| 105 | + |
| 106 | +For counts greater than 1, `Assert.Equal(expectedCount, collection.Count)` is acceptable. |
| 107 | + |
| 108 | +### Test Organization with Regions |
| 109 | + |
| 110 | +Organize unit tests within a test class using `#region` directives to group tests by the method or property under test: |
| 111 | + |
| 112 | +```csharp |
| 113 | +public class MyClassTests |
| 114 | +{ |
| 115 | + #region Constructor Tests |
| 116 | + |
| 117 | + [Fact] |
| 118 | + public void Constructor_WithValidInput_CreatesInstance() |
| 119 | + { |
| 120 | + // test code |
| 121 | + } |
| 122 | + |
| 123 | + [Fact] |
| 124 | + public void Constructor_WithNullInput_ThrowsException() |
| 125 | + { |
| 126 | + // test code |
| 127 | + } |
| 128 | + |
| 129 | + #endregion |
| 130 | + |
| 131 | + #region MyMethod Tests |
| 132 | + |
| 133 | + [Fact] |
| 134 | + public void MyMethod_WhenCondition_ExpectedBehavior() |
| 135 | + { |
| 136 | + // test code |
| 137 | + } |
| 138 | + |
| 139 | + #endregion |
| 140 | + |
| 141 | + #region MyProperty Tests |
| 142 | + |
| 143 | + [Fact] |
| 144 | + public void MyProperty_ReturnsExpectedValue() |
| 145 | + { |
| 146 | + // test code |
| 147 | + } |
| 148 | + |
| 149 | + #endregion |
| 150 | +} |
| 151 | +``` |
| 152 | + |
| 153 | +Region naming conventions: |
| 154 | +- Use `Constructor Tests` for constructor tests |
| 155 | +- Use `{MethodName} Tests` for method tests (e.g., `Dispose Tests`) |
| 156 | +- Use `{PropertyName} Property Tests` for property tests (e.g., `Span Property Tests`) |
| 157 | +- Use `Generic Type Tests` for tests verifying generic type parameter behavior |
0 commit comments