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
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
]
},
"demaconsulting.buildmark": {
"version": "0.2.0",
"version": "0.3.0",
"commands": [
"buildmark"
]
Expand Down
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ root = true
# All files
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
insert_final_newline = true
Expand Down
35 changes: 35 additions & 0 deletions .github/agents/test-developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,41 @@ public void ClassName_MethodUnderTest_Scenario_ExpectedBehavior()
- Use MSTest V4 testing framework
- Follow existing naming conventions in the test suite

### MSTest V4 Best Practices

Common anti-patterns to avoid (not exhaustive):

1. **Avoid Assertions in Catch Blocks (MSTEST0058)** - Instead of wrapping code in try/catch and asserting in the
catch block, use `Assert.ThrowsExactly<T>()`:

```csharp
var ex = Assert.ThrowsExactly<ArgumentNullException>(() => SomeWork());
Assert.Contains("Some message", ex.Message);
```

2. **Avoid using Assert.IsTrue / Assert.IsFalse for equality checks** - Use `Assert.AreEqual` /
`Assert.AreNotEqual` instead, as it provides better failure messages:

```csharp
// ❌ Bad: Assert.IsTrue(result == expected);
// ✅ Good: Assert.AreEqual(expected, result);
```

3. **Avoid non-public test classes and methods** - Test classes and `[TestMethod]` methods must be `public` or
they will be silently ignored:

```csharp
// ❌ Bad: internal class MyTests
// ✅ Good: public class MyTests
```

4. **Avoid Assert.IsTrue(collection.Count == N)** - Use `Assert.HasCount` for count assertions:

```csharp
// ❌ Bad: Assert.IsTrue(collection.Count == 3);
// ✅ Good: Assert.HasCount(3, collection);
```

## Defer To

- **Requirements Agent**: For test strategy and coverage requirements
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ artifacts/

# Node.js
node_modules/
package-lock.json
npm-debug.log

# Python
Expand Down
3 changes: 0 additions & 3 deletions test/DemaConsulting.TemplateDotNetTool.Tests/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,4 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("DemaConsulting.TemplateDotNetTool.Tests")]
[assembly: DoNotParallelize]
28 changes: 28 additions & 0 deletions test/DemaConsulting.TemplateDotNetTool.Tests/ContextTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ public class ContextTests
[TestMethod]
public void Context_Create_NoArguments_ReturnsDefaultContext()
{
// Act
using var context = Context.Create([]);

// Assert
Assert.IsFalse(context.Version);
Assert.IsFalse(context.Help);
Assert.IsFalse(context.Silent);
Expand All @@ -47,8 +49,10 @@ public void Context_Create_NoArguments_ReturnsDefaultContext()
[TestMethod]
public void Context_Create_VersionFlag_SetsVersionTrue()
{
// Act
using var context = Context.Create(["--version"]);

// Assert
Assert.IsTrue(context.Version);
Assert.IsFalse(context.Help);
Assert.AreEqual(0, context.ExitCode);
Expand All @@ -60,8 +64,10 @@ public void Context_Create_VersionFlag_SetsVersionTrue()
[TestMethod]
public void Context_Create_ShortVersionFlag_SetsVersionTrue()
{
// Act
using var context = Context.Create(["-v"]);

// Assert
Assert.IsTrue(context.Version);
Assert.IsFalse(context.Help);
Assert.AreEqual(0, context.ExitCode);
Expand All @@ -73,8 +79,10 @@ public void Context_Create_ShortVersionFlag_SetsVersionTrue()
[TestMethod]
public void Context_Create_HelpFlag_SetsHelpTrue()
{
// Act
using var context = Context.Create(["--help"]);

// Assert
Assert.IsFalse(context.Version);
Assert.IsTrue(context.Help);
Assert.AreEqual(0, context.ExitCode);
Expand All @@ -86,8 +94,10 @@ public void Context_Create_HelpFlag_SetsHelpTrue()
[TestMethod]
public void Context_Create_ShortHelpFlag_H_SetsHelpTrue()
{
// Act
using var context = Context.Create(["-h"]);

// Assert
Assert.IsFalse(context.Version);
Assert.IsTrue(context.Help);
Assert.AreEqual(0, context.ExitCode);
Expand All @@ -99,8 +109,10 @@ public void Context_Create_ShortHelpFlag_H_SetsHelpTrue()
[TestMethod]
public void Context_Create_ShortHelpFlag_Question_SetsHelpTrue()
{
// Act
using var context = Context.Create(["-?"]);

// Assert
Assert.IsFalse(context.Version);
Assert.IsTrue(context.Help);
Assert.AreEqual(0, context.ExitCode);
Expand All @@ -112,8 +124,10 @@ public void Context_Create_ShortHelpFlag_Question_SetsHelpTrue()
[TestMethod]
public void Context_Create_SilentFlag_SetsSilentTrue()
{
// Act
using var context = Context.Create(["--silent"]);

// Assert
Assert.IsTrue(context.Silent);
Assert.AreEqual(0, context.ExitCode);
}
Expand All @@ -124,8 +138,10 @@ public void Context_Create_SilentFlag_SetsSilentTrue()
[TestMethod]
public void Context_Create_ValidateFlag_SetsValidateTrue()
{
// Act
using var context = Context.Create(["--validate"]);

// Assert
Assert.IsTrue(context.Validate);
Assert.AreEqual(0, context.ExitCode);
}
Expand All @@ -136,8 +152,10 @@ public void Context_Create_ValidateFlag_SetsValidateTrue()
[TestMethod]
public void Context_Create_ResultsFlag_SetsResultsFile()
{
// Act
using var context = Context.Create(["--results", "test.trx"]);

// Assert
Assert.AreEqual("test.trx", context.ResultsFile);
Assert.AreEqual(0, context.ExitCode);
}
Expand All @@ -148,15 +166,18 @@ public void Context_Create_ResultsFlag_SetsResultsFile()
[TestMethod]
public void Context_Create_LogFlag_OpensLogFile()
{
// Arrange
var logFile = Path.GetTempFileName();
try
{
// Act
using (var context = Context.Create(["--log", logFile]))
{
context.WriteLine("Test message");
Assert.AreEqual(0, context.ExitCode);
}

// Assert
// Verify log file was written
Assert.IsTrue(File.Exists(logFile));
var logContent = File.ReadAllText(logFile);
Expand All @@ -177,6 +198,7 @@ public void Context_Create_LogFlag_OpensLogFile()
[TestMethod]
public void Context_Create_UnknownArgument_ThrowsArgumentException()
{
// Act & Assert
var exception = Assert.Throws<ArgumentException>(() => Context.Create(["--unknown"]));
Assert.Contains("Unsupported argument", exception.Message);
}
Expand All @@ -187,15 +209,18 @@ public void Context_Create_UnknownArgument_ThrowsArgumentException()
[TestMethod]
public void Context_WriteLine_NotSilent_WritesToConsole()
{
// Arrange
var originalOut = Console.Out;
try
{
using var outWriter = new StringWriter();
Console.SetOut(outWriter);
using var context = Context.Create([]);

// Act
context.WriteLine("Test message");

// Assert
var output = outWriter.ToString();
Assert.Contains("Test message", output);
}
Expand All @@ -211,15 +236,18 @@ public void Context_WriteLine_NotSilent_WritesToConsole()
[TestMethod]
public void Context_WriteLine_Silent_DoesNotWriteToConsole()
{
// Arrange
var originalOut = Console.Out;
try
{
using var outWriter = new StringWriter();
Console.SetOut(outWriter);
using var context = Context.Create(["--silent"]);

// Act
context.WriteLine("Test message");

// Assert
var output = outWriter.ToString();
Assert.DoesNotContain("Test message", output);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<!-- Build Configuration -->
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<LangVersion>12</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>latest</AnalysisLevel>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<GenerateDocumentationFile>True</GenerateDocumentationFile>

<!-- Code Quality Configuration -->
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>latest</AnalysisLevel>
</PropertyGroup>

<!-- Test Framework Dependencies -->
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="MSTest.TestAdapter" Version="4.1.0" />
<PackageReference Include="MSTest.TestFramework" Version="4.1.0" />
</ItemGroup>

<!-- Code Analysis Dependencies -->
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.103">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="MSTest.TestAdapter" Version="4.1.0" />
<PackageReference Include="MSTest.TestFramework" Version="4.1.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.19.0.132793">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<!-- Project References -->
<ItemGroup>
<ProjectReference Include="..\..\src\DemaConsulting.TemplateDotNetTool\DemaConsulting.TemplateDotNetTool.csproj" />
</ItemGroup>
Expand Down
Loading