diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 0000000..e5d23d9 --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,91 @@ +name: .NET Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +defaults: + run: + working-directory: dotnet-examples + +jobs: + test: + name: .NET TRX Reports + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "8.0.x" + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore + + - name: Create reports directory + run: mkdir -p reports + + - name: Run xUnit tests + run: dotnet test tests/Calculator.XUnit.Tests --no-build --logger "trx;LogFileName=xunit-results.trx" --results-directory ./reports + continue-on-error: true + + - name: Run NUnit tests + run: dotnet test tests/Calculator.NUnit.Tests --no-build --logger "trx;LogFileName=nunit-results.trx" --results-directory ./reports + continue-on-error: true + + - name: Run MSTest tests + run: dotnet test tests/Calculator.MSTest.Tests --no-build --logger "trx;LogFileName=mstest-results.trx" --results-directory ./reports + continue-on-error: true + + # Upload xUnit TRX to Gaffer + - name: Upload xUnit TRX to Gaffer + if: always() && github.actor != 'dependabot[bot]' + uses: gaffer-sh/gaffer-uploader@v0.3.0 + with: + gaffer_api_key: ${{ secrets.GAFFER_API_KEY }} + report_path: dotnet-examples/reports/xunit-results.trx + commit_sha: ${{ github.event.pull_request.head.sha || github.sha }} + branch: ${{ github.ref_name }} + test_framework: dotnet + test_suite: xunit + + # Upload NUnit TRX to Gaffer + - name: Upload NUnit TRX to Gaffer + if: always() && github.actor != 'dependabot[bot]' + uses: gaffer-sh/gaffer-uploader@v0.3.0 + with: + gaffer_api_key: ${{ secrets.GAFFER_API_KEY }} + report_path: dotnet-examples/reports/nunit-results.trx + commit_sha: ${{ github.event.pull_request.head.sha || github.sha }} + branch: ${{ github.ref_name }} + test_framework: dotnet + test_suite: nunit + + # Upload MSTest TRX to Gaffer + - name: Upload MSTest TRX to Gaffer + if: always() && github.actor != 'dependabot[bot]' + uses: gaffer-sh/gaffer-uploader@v0.3.0 + with: + gaffer_api_key: ${{ secrets.GAFFER_API_KEY }} + report_path: dotnet-examples/reports/mstest-results.trx + commit_sha: ${{ github.event.pull_request.head.sha || github.sha }} + branch: ${{ github.ref_name }} + test_framework: dotnet + test_suite: mstest + + # Store artifacts for parser development + - name: Upload test artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: dotnet-reports-${{ github.sha }} + path: dotnet-examples/reports/ + retention-days: 30 diff --git a/README.md b/README.md index a1543d8..163a5c4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Example test projects demonstrating [Gaffer](https://gaffer.sh) integration for various test frameworks. -Parser Check: December 4 2025 - 20:41 +Parser Check: December 17 2025 - 8:31 ## Examples @@ -11,6 +11,7 @@ Parser Check: December 4 2025 - 20:41 | Jest | JSON, HTML | [`jest-example/`](./jest-example/) | | pytest | HTML | [`pytest-example/`](./pytest-example/) | | Vitest | JSON | [`vitest-example/`](./vitest-example/) | +| .NET (xUnit, NUnit, MSTest) | TRX | [`dotnet-examples/`](./dotnet-examples/) | ## Quick Start @@ -18,6 +19,7 @@ Parser Check: December 4 2025 - 20:41 - Node.js 22+ (Jest, Vitest) - Python 3.9+ (pytest) +- Docker or .NET 8.0 SDK (xUnit, NUnit, MSTest) - [Gaffer API key](https://app.gaffer.sh) ### Running Examples Locally @@ -46,6 +48,19 @@ npm install npm run test:json # Generates JSON report ``` +#### .NET (via Docker) + +```bash +cd dotnet-examples +mkdir -p reports +docker run --rm -v "$(pwd):/app" -w /app mcr.microsoft.com/dotnet/sdk:8.0 sh -c " + dotnet restore && dotnet build && \ + dotnet test tests/Calculator.XUnit.Tests --logger 'trx;LogFileName=xunit-results.trx' --results-directory ./reports && \ + dotnet test tests/Calculator.NUnit.Tests --logger 'trx;LogFileName=nunit-results.trx' --results-directory ./reports && \ + dotnet test tests/Calculator.MSTest.Tests --logger 'trx;LogFileName=mstest-results.trx' --results-directory ./reports +" +``` + ## Uploading to Gaffer ### Using the GitHub Action (Recommended) @@ -90,6 +105,9 @@ See the workflows in [`.github/workflows/`](./.github/workflows/). | Jest | HTML | `reports/jest-report.html` | `jest-html` | | pytest | HTML | `reports/pytest-report.html` | `pytest-html` | | Vitest | JSON | `reports/vitest-results.json` | `vitest-json` | +| xUnit | TRX | `reports/xunit-results.trx` | `trx` | +| NUnit | TRX | `reports/nunit-results.trx` | `trx` | +| MSTest | TRX | `reports/mstest-results.trx` | `trx` | ## Accessing Artifacts diff --git a/dotnet-examples/.gitignore b/dotnet-examples/.gitignore new file mode 100644 index 0000000..b101870 --- /dev/null +++ b/dotnet-examples/.gitignore @@ -0,0 +1,17 @@ +# Build outputs +bin/ +obj/ + +# Reports (generated) +reports/ +*.trx + +# IDE +.vs/ +.idea/ +*.user +*.suo + +# OS +.DS_Store +Thumbs.db diff --git a/dotnet-examples/DotnetExamples.sln b/dotnet-examples/DotnetExamples.sln new file mode 100644 index 0000000..f2e7fc2 --- /dev/null +++ b/dotnet-examples/DotnetExamples.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator", "src\Calculator\Calculator.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.XUnit.Tests", "tests\Calculator.XUnit.Tests\Calculator.XUnit.Tests.csproj", "{B2C3D4E5-F6A7-8901-BCDE-F12345678901}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.NUnit.Tests", "tests\Calculator.NUnit.Tests\Calculator.NUnit.Tests.csproj", "{C3D4E5F6-A7B8-9012-CDEF-123456789012}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.MSTest.Tests", "tests\Calculator.MSTest.Tests\Calculator.MSTest.Tests.csproj", "{D4E5F6A7-B8C9-0123-DEF0-234567890123}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.Build.0 = Release|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.Build.0 = Release|Any CPU + {D4E5F6A7-B8C9-0123-DEF0-234567890123}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4E5F6A7-B8C9-0123-DEF0-234567890123}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4E5F6A7-B8C9-0123-DEF0-234567890123}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4E5F6A7-B8C9-0123-DEF0-234567890123}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/dotnet-examples/README.md b/dotnet-examples/README.md new file mode 100644 index 0000000..4905f20 --- /dev/null +++ b/dotnet-examples/README.md @@ -0,0 +1,142 @@ +# .NET Test Examples + +Example .NET 8.0 test projects demonstrating TRX report generation for [Gaffer](https://gaffer.sh). + +This directory contains three test projects using different .NET test frameworks: +- **xUnit** - Popular open-source testing framework +- **NUnit** - Well-established testing framework +- **MSTest** - Microsoft's official testing framework + +## Prerequisites + +- [Docker](https://www.docker.com/get-started) (recommended) +- OR [.NET 8.0 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) +- [Gaffer API key](https://app.gaffer.sh) + +## Running Tests with Docker (Recommended) + +No local .NET installation required. + +```bash +cd dotnet-examples + +# Create reports directory +mkdir -p reports + +# Run all tests and generate TRX reports +docker run --rm -v "$(pwd):/app" -w /app mcr.microsoft.com/dotnet/sdk:8.0 sh -c " + dotnet restore && \ + dotnet build && \ + dotnet test tests/Calculator.XUnit.Tests --logger 'trx;LogFileName=xunit-results.trx' --results-directory ./reports && \ + dotnet test tests/Calculator.NUnit.Tests --logger 'trx;LogFileName=nunit-results.trx' --results-directory ./reports && \ + dotnet test tests/Calculator.MSTest.Tests --logger 'trx;LogFileName=mstest-results.trx' --results-directory ./reports +" +``` + +### Run individual frameworks + +```bash +# xUnit only +docker run --rm -v "$(pwd):/app" -w /app mcr.microsoft.com/dotnet/sdk:8.0 \ + dotnet test tests/Calculator.XUnit.Tests --logger "trx;LogFileName=xunit-results.trx" --results-directory ./reports + +# NUnit only +docker run --rm -v "$(pwd):/app" -w /app mcr.microsoft.com/dotnet/sdk:8.0 \ + dotnet test tests/Calculator.NUnit.Tests --logger "trx;LogFileName=nunit-results.trx" --results-directory ./reports + +# MSTest only +docker run --rm -v "$(pwd):/app" -w /app mcr.microsoft.com/dotnet/sdk:8.0 \ + dotnet test tests/Calculator.MSTest.Tests --logger "trx;LogFileName=mstest-results.trx" --results-directory ./reports +``` + +## Running Tests with Local .NET SDK + +```bash +cd dotnet-examples +dotnet restore +mkdir -p reports + +# Run xUnit tests +dotnet test tests/Calculator.XUnit.Tests --logger "trx;LogFileName=xunit-results.trx" --results-directory ./reports + +# Run NUnit tests +dotnet test tests/Calculator.NUnit.Tests --logger "trx;LogFileName=nunit-results.trx" --results-directory ./reports + +# Run MSTest tests +dotnet test tests/Calculator.MSTest.Tests --logger "trx;LogFileName=mstest-results.trx" --results-directory ./reports + +# Or run all at once +dotnet test --logger "trx;LogFileName=test-results.trx" --results-directory reports +``` + +## Report Output + +| Framework | Output Path | +|-----------|-------------| +| xUnit | `reports/xunit-results.trx` | +| NUnit | `reports/nunit-results.trx` | +| MSTest | `reports/mstest-results.trx` | + +## TRX Format Notes + +TRX (Test Results XML) is the native .NET test report format. Key characteristics: +- Root element: `` +- File extension: `.trx` +- Test results in `` elements +- Outcomes: `Passed`, `Failed`, `NotExecuted`, `Inconclusive` +- Duration in `HH:mm:ss.fffffff` format +- Error details in `` and `` + +## Test Cases + +Each test project includes various test types to demonstrate TRX output: + +| Test Type | xUnit | NUnit | MSTest | TRX Outcome | +|-----------|-------|-------|--------|-------------| +| Passing tests | `[Fact]` | `[Test]` | `[TestMethod]` | `Passed` | +| Failing test | `[Fact]` | `[Test]` | `[TestMethod]` | `Failed` | +| Skipped test | `[Fact(Skip="...")]` | `[Ignore("...")]` | `[Ignore("...")]` | `NotExecuted` | +| Inconclusive | N/A | `Assert.Inconclusive()` | `Assert.Inconclusive()` | `Inconclusive` | +| Parameterized | `[Theory]` | `[TestCase]` | `[DataRow]` | Multiple results | +| Exception tests | `Assert.Throws<>` | `Assert.Throws<>` | `[ExpectedException]` | `Passed` | + +## Uploading to Gaffer + +### Using curl + +```bash +# Upload xUnit TRX +curl -X POST https://app.gaffer.sh/api/upload \ + -H "X-API-Key: $GAFFER_API_KEY" \ + -F "files=@reports/xunit-results.trx" + +# Upload NUnit TRX +curl -X POST https://app.gaffer.sh/api/upload \ + -H "X-API-Key: $GAFFER_API_KEY" \ + -F "files=@reports/nunit-results.trx" + +# Upload MSTest TRX +curl -X POST https://app.gaffer.sh/api/upload \ + -H "X-API-Key: $GAFFER_API_KEY" \ + -F "files=@reports/mstest-results.trx" +``` + +### Using GitHub Action + +See `.github/workflows/dotnet.yml` for the full CI/CD workflow. + +## Project Structure + +``` +dotnet-examples/ +├── src/Calculator/ # Shared source code +│ ├── Calculator.cs +│ └── Calculator.csproj +├── tests/ +│ ├── Calculator.XUnit.Tests/ +│ ├── Calculator.NUnit.Tests/ +│ └── Calculator.MSTest.Tests/ +├── DotnetExamples.sln +├── .gitignore +└── README.md +``` diff --git a/dotnet-examples/src/Calculator/Calculator.cs b/dotnet-examples/src/Calculator/Calculator.cs new file mode 100644 index 0000000..8c56d7c --- /dev/null +++ b/dotnet-examples/src/Calculator/Calculator.cs @@ -0,0 +1,35 @@ +namespace Calculator; + +public class Calculator +{ + public double Add(double a, double b) => a + b; + + public double Subtract(double a, double b) => a - b; + + public double Multiply(double a, double b) => a * b; + + public double Divide(double a, double b) + { + if (b == 0) + throw new DivideByZeroException("Cannot divide by zero"); + return a / b; + } +} + +public static class StringUtils +{ + public static string Reverse(string input) + { + if (input == null) + throw new ArgumentNullException(nameof(input)); + return new string(input.Reverse().ToArray()); + } + + public static bool IsPalindrome(string input) + { + if (input == null) + throw new ArgumentNullException(nameof(input)); + var cleaned = input.ToLower().Replace(" ", ""); + return cleaned == new string(cleaned.Reverse().ToArray()); + } +} diff --git a/dotnet-examples/src/Calculator/Calculator.csproj b/dotnet-examples/src/Calculator/Calculator.csproj new file mode 100644 index 0000000..e8cd599 --- /dev/null +++ b/dotnet-examples/src/Calculator/Calculator.csproj @@ -0,0 +1,7 @@ + + + net8.0 + enable + enable + + diff --git a/dotnet-examples/tests/Calculator.MSTest.Tests/Calculator.MSTest.Tests.csproj b/dotnet-examples/tests/Calculator.MSTest.Tests/Calculator.MSTest.Tests.csproj new file mode 100644 index 0000000..8276d42 --- /dev/null +++ b/dotnet-examples/tests/Calculator.MSTest.Tests/Calculator.MSTest.Tests.csproj @@ -0,0 +1,21 @@ + + + net8.0 + enable + enable + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + diff --git a/dotnet-examples/tests/Calculator.MSTest.Tests/CalculatorTests.cs b/dotnet-examples/tests/Calculator.MSTest.Tests/CalculatorTests.cs new file mode 100644 index 0000000..d88aca2 --- /dev/null +++ b/dotnet-examples/tests/Calculator.MSTest.Tests/CalculatorTests.cs @@ -0,0 +1,115 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Calculator.MSTest.Tests; + +[TestClass] +public class CalculatorTests +{ + private Calculator _calculator = null!; + + [TestInitialize] + public void Setup() + { + _calculator = new Calculator(); + } + + // PASSING TESTS + [TestMethod] + public void Add_TwoPositiveNumbers_ReturnsSum() + { + Assert.AreEqual(5.0, _calculator.Add(2, 3)); + } + + [TestMethod] + public void Add_NegativeNumbers_ReturnsCorrectSum() + { + Assert.AreEqual(0.0, _calculator.Add(-1, 1)); + } + + [TestMethod] + public void Subtract_TwoNumbers_ReturnsDifference() + { + Assert.AreEqual(2.0, _calculator.Subtract(5, 3)); + } + + [TestMethod] + public void Multiply_TwoNumbers_ReturnsProduct() + { + Assert.AreEqual(6.0, _calculator.Multiply(2, 3)); + } + + [TestMethod] + public void Divide_TwoNumbers_ReturnsQuotient() + { + Assert.AreEqual(3.0, _calculator.Divide(6, 2)); + } + + [TestMethod] + [ExpectedException(typeof(DivideByZeroException))] + public void Divide_ByZero_ThrowsDivideByZeroException() + { + _calculator.Divide(5, 0); + } + + // FAILING TEST (intentional for TRX parser testing) + [TestMethod] + public void FailingTest_DemonstratesFailure() + { + // This test intentionally fails to generate error output in TRX + Assert.AreEqual(100.0, _calculator.Add(1, 1)); + } + + // SKIPPED/IGNORED TEST + [TestMethod] + [Ignore("Skipped for demonstration - feature not yet implemented")] + public void SkippedTest_DemonstratesIgnore() + { + Assert.Fail(); + } + + // PARAMETERIZED TEST (DataRow) + [TestMethod] + [DataRow(1.0, 2.0, 3.0)] + [DataRow(-1.0, -1.0, -2.0)] + [DataRow(0.0, 0.0, 0.0)] + public void Add_MultipleInputs_ReturnsExpectedSum(double a, double b, double expected) + { + Assert.AreEqual(expected, _calculator.Add(a, b)); + } + + // INCONCLUSIVE TEST + [TestMethod] + public void InconclusiveTest_DemonstratesInconclusive() + { + Assert.Inconclusive("Test result is inconclusive - requires manual verification"); + } +} + +[TestClass] +public class StringUtilsTests +{ + [TestMethod] + public void Reverse_ValidString_ReturnsReversed() + { + Assert.AreEqual("olleh", StringUtils.Reverse("hello")); + } + + [TestMethod] + public void IsPalindrome_Palindrome_ReturnsTrue() + { + Assert.IsTrue(StringUtils.IsPalindrome("racecar")); + } + + [TestMethod] + public void IsPalindrome_NonPalindrome_ReturnsFalse() + { + Assert.IsFalse(StringUtils.IsPalindrome("hello")); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void Reverse_NullInput_ThrowsArgumentNullException() + { + StringUtils.Reverse(null!); + } +} diff --git a/dotnet-examples/tests/Calculator.NUnit.Tests/Calculator.NUnit.Tests.csproj b/dotnet-examples/tests/Calculator.NUnit.Tests/Calculator.NUnit.Tests.csproj new file mode 100644 index 0000000..2904941 --- /dev/null +++ b/dotnet-examples/tests/Calculator.NUnit.Tests/Calculator.NUnit.Tests.csproj @@ -0,0 +1,21 @@ + + + net8.0 + enable + enable + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + diff --git a/dotnet-examples/tests/Calculator.NUnit.Tests/CalculatorTests.cs b/dotnet-examples/tests/Calculator.NUnit.Tests/CalculatorTests.cs new file mode 100644 index 0000000..5d01d98 --- /dev/null +++ b/dotnet-examples/tests/Calculator.NUnit.Tests/CalculatorTests.cs @@ -0,0 +1,112 @@ +using NUnit.Framework; + +namespace Calculator.NUnit.Tests; + +[TestFixture] +public class CalculatorTests +{ + private Calculator _calculator = null!; + + [SetUp] + public void Setup() + { + _calculator = new Calculator(); + } + + // PASSING TESTS + [Test] + public void Add_TwoPositiveNumbers_ReturnsSum() + { + Assert.That(_calculator.Add(2, 3), Is.EqualTo(5)); + } + + [Test] + public void Add_NegativeNumbers_ReturnsCorrectSum() + { + Assert.That(_calculator.Add(-1, 1), Is.EqualTo(0)); + } + + [Test] + public void Subtract_TwoNumbers_ReturnsDifference() + { + Assert.That(_calculator.Subtract(5, 3), Is.EqualTo(2)); + } + + [Test] + public void Multiply_TwoNumbers_ReturnsProduct() + { + Assert.That(_calculator.Multiply(2, 3), Is.EqualTo(6)); + } + + [Test] + public void Divide_TwoNumbers_ReturnsQuotient() + { + Assert.That(_calculator.Divide(6, 2), Is.EqualTo(3)); + } + + [Test] + public void Divide_ByZero_ThrowsDivideByZeroException() + { + Assert.Throws(() => _calculator.Divide(5, 0)); + } + + // FAILING TEST (intentional for TRX parser testing) + [Test] + public void FailingTest_DemonstratesFailure() + { + // This test intentionally fails to generate error output in TRX + Assert.That(_calculator.Add(1, 1), Is.EqualTo(100)); + } + + // SKIPPED/IGNORED TEST + [Test] + [Ignore("Skipped for demonstration - feature not yet implemented")] + public void SkippedTest_DemonstratesIgnore() + { + Assert.Fail(); + } + + // PARAMETERIZED TEST (TestCase) + [TestCase(1, 2, 3)] + [TestCase(-1, -1, -2)] + [TestCase(0, 0, 0)] + public void Add_MultipleInputs_ReturnsExpectedSum(double a, double b, double expected) + { + Assert.That(_calculator.Add(a, b), Is.EqualTo(expected)); + } + + // INCONCLUSIVE TEST (maps to Inconclusive in TRX) + [Test] + public void InconclusiveTest_DemonstratesInconclusive() + { + Assert.Inconclusive("Test result is inconclusive - requires manual verification"); + } +} + +[TestFixture] +public class StringUtilsTests +{ + [Test] + public void Reverse_ValidString_ReturnsReversed() + { + Assert.That(StringUtils.Reverse("hello"), Is.EqualTo("olleh")); + } + + [Test] + public void IsPalindrome_Palindrome_ReturnsTrue() + { + Assert.That(StringUtils.IsPalindrome("racecar"), Is.True); + } + + [Test] + public void IsPalindrome_NonPalindrome_ReturnsFalse() + { + Assert.That(StringUtils.IsPalindrome("hello"), Is.False); + } + + [Test] + public void Reverse_NullInput_ThrowsArgumentNullException() + { + Assert.Throws(() => StringUtils.Reverse(null!)); + } +} diff --git a/dotnet-examples/tests/Calculator.XUnit.Tests/Calculator.XUnit.Tests.csproj b/dotnet-examples/tests/Calculator.XUnit.Tests/Calculator.XUnit.Tests.csproj new file mode 100644 index 0000000..a9a2f93 --- /dev/null +++ b/dotnet-examples/tests/Calculator.XUnit.Tests/Calculator.XUnit.Tests.csproj @@ -0,0 +1,24 @@ + + + net8.0 + enable + enable + false + true + + + + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + + + + diff --git a/dotnet-examples/tests/Calculator.XUnit.Tests/CalculatorTests.cs b/dotnet-examples/tests/Calculator.XUnit.Tests/CalculatorTests.cs new file mode 100644 index 0000000..0b4d0e7 --- /dev/null +++ b/dotnet-examples/tests/Calculator.XUnit.Tests/CalculatorTests.cs @@ -0,0 +1,97 @@ +using Xunit; + +namespace Calculator.XUnit.Tests; + +public class CalculatorTests +{ + private readonly Calculator _calculator = new(); + + // PASSING TESTS + [Fact] + public void Add_TwoPositiveNumbers_ReturnsSum() + { + Assert.Equal(5, _calculator.Add(2, 3)); + } + + [Fact] + public void Add_NegativeNumbers_ReturnsCorrectSum() + { + Assert.Equal(0, _calculator.Add(-1, 1)); + } + + [Fact] + public void Subtract_TwoNumbers_ReturnsDifference() + { + Assert.Equal(2, _calculator.Subtract(5, 3)); + } + + [Fact] + public void Multiply_TwoNumbers_ReturnsProduct() + { + Assert.Equal(6, _calculator.Multiply(2, 3)); + } + + [Fact] + public void Divide_TwoNumbers_ReturnsQuotient() + { + Assert.Equal(3, _calculator.Divide(6, 2)); + } + + [Fact] + public void Divide_ByZero_ThrowsDivideByZeroException() + { + Assert.Throws(() => _calculator.Divide(5, 0)); + } + + // FAILING TEST (intentional for TRX parser testing) + [Fact] + public void FailingTest_DemonstratesFailure() + { + // This test intentionally fails to generate error output in TRX + Assert.Equal(100, _calculator.Add(1, 1)); + } + + // SKIPPED TEST + [Fact(Skip = "Skipped for demonstration - feature not yet implemented")] + public void SkippedTest_DemonstratesSkip() + { + Assert.True(false); + } + + // THEORY (parameterized test) + [Theory] + [InlineData(1, 2, 3)] + [InlineData(-1, -1, -2)] + [InlineData(0, 0, 0)] + public void Add_MultipleInputs_ReturnsExpectedSum(double a, double b, double expected) + { + Assert.Equal(expected, _calculator.Add(a, b)); + } +} + +public class StringUtilsTests +{ + [Fact] + public void Reverse_ValidString_ReturnsReversed() + { + Assert.Equal("olleh", StringUtils.Reverse("hello")); + } + + [Fact] + public void IsPalindrome_Palindrome_ReturnsTrue() + { + Assert.True(StringUtils.IsPalindrome("racecar")); + } + + [Fact] + public void IsPalindrome_NonPalindrome_ReturnsFalse() + { + Assert.False(StringUtils.IsPalindrome("hello")); + } + + [Fact] + public void Reverse_NullInput_ThrowsArgumentNullException() + { + Assert.Throws(() => StringUtils.Reverse(null!)); + } +}