Claude Skill

dotnet-xunit

Write, run, or repair .NET tests that use xUnit. Use when a repo uses `xunit`, `xunit.v3`, `[Fact]`, `[Theory]`, or `xunit.runner.visualstudio`, and you need the right CLI, package, and runner guidance for xUnit on VSTest or Microsoft.Testing.Platform.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download postpartum-genushyacinthus29-dotnet-skills-skills_dotnet-xunit-bfa4ebd.zip · 10 KB
Part of postpartum-genushyacinthus29/dotnet-skills — 80 skills

Install

skills CLI npx skills add https://github.com/Postpartum-genushyacinthus29/dotnet-skills/tree/main/skills/dotnet-xunit
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install postpartum-genushyacinthus29-dotnet-skills@llmmart
Git git clone https://github.com/Postpartum-genushyacinthus29/dotnet-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole postpartum-genushyacinthus29/dotnet-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

xUnit.net

Trigger On

  • the repo uses xUnit v2 or xUnit v3
  • you need to add, run, debug, or repair xUnit tests
  • the team is unsure whether a project is using VSTest or Microsoft.Testing.Platform

Value

  • produce a concrete project delta: code, docs, config, tests, CI, or review artifact
  • reduce ambiguity through explicit planning, verification, and final validation skills
  • leave reusable project context so future tasks are faster and safer

Do Not Use For

  • TUnit projects
  • MSTest projects
  • generic test strategy with no xUnit-specific mechanics

Inputs

  • the nearest AGENTS.md
  • the test project file and package references
  • the active runner model for the test project

Quick Start

  1. Read the nearest AGENTS.md and confirm scope and constraints.
  2. Run this skill's Workflow through the Ralph Loop until outcomes are acceptable.
  3. Return the Required Result Format with concrete artifacts and verification evidence.

Workflow

  1. Detect the active xUnit model before changing commands:
    • xunit usually means v2
    • xunit.v3 means v3
    • xunit.runner.visualstudio plus Microsoft.NET.Test.Sdk usually means VSTest compatibility is enabled
    • TestingPlatformDotnetTestSupport or UseMicrosoftTestingPlatformRunner means Microsoft.Testing.Platform is in play
  2. Read the repo's real test command from AGENTS.md. If the repo has no explicit command yet, start with dotnet test PROJECT_OR_SOLUTION.
  3. Keep the runner model consistent:
    • xUnit v2 usually runs through VSTest
    • xUnit v3 can run as a standalone executable with dotnet run
    • xUnit v3 can also integrate with Microsoft.Testing.Platform
    • do not mix VSTest-only switches into Microsoft.Testing.Platform runs
  4. Run the narrowest useful scope first:
    • one project
    • one class
    • one trait
    • one method
  5. Prefer [Theory] for stable data-driven coverage and [Fact] for single-path invariant checks.
  6. Keep xunit.analyzers enabled when present. Fix analyzer findings instead of muting them casually.

Bootstrap When Missing

If xUnit is requested but not configured:

  1. Detect current framework first:
    • rg -n "xunit(\\.v3)?|xunit\\.runner\\.visualstudio|TestingPlatformDotnetTestSupport|UseMicrosoftTestingPlatformRunner|TUnit|MSTest" -g '*.csproj' .
  2. If the repo currently uses TUnit or MSTest, do not auto-migrate. Return status: not_applicable unless migration is explicitly requested.
  3. For explicit xUnit adoption, add packages to the target test project:
    • dotnet add TEST_PROJECT.csproj package xunit.v3
    • optional VSTest bridge: dotnet add TEST_PROJECT.csproj package xunit.runner.visualstudio
  4. Add repo test commands and runner notes to AGENTS.md.
  5. Run dotnet test TEST_PROJECT.csproj or repo-defined xUnit command and return status: configured or status: improved.

Deliver

  • xUnit tests that match the repo's active xUnit version and runner
  • commands that work in local and CI runs
  • focused verification before broader suite execution

Validate

  • the chosen CLI matches the active runner model
  • test filters or focused runs are valid for that runner
  • tests use deterministic inputs and assertions
  • xUnit-specific analyzers remain active unless the repo documents an exception

Ralph Loop

Use the Ralph Loop for every task, including docs, architecture, testing, and tooling work.

  1. Plan first (mandatory):
    • analyze current state
    • define target outcome, constraints, and risks
    • write a detailed execution plan
    • list final validation skills to run at the end, with order and reason
  2. Execute one planned step and produce a concrete delta.
  3. Review the result and capture findings with actionable next fixes.
  4. Apply fixes in small batches and rerun the relevant checks or review steps.
  5. Update the plan after each iteration.
  6. Repeat until outcomes are acceptable or only explicit exceptions remain.
  7. If a dependency is missing, bootstrap it or return status: not_applicable with explicit reason and fallback path.

Required Result Format

  • status: complete | clean | improved | configured | not_applicable | blocked
  • plan: concise plan and current iteration step
  • actions_taken: concrete changes made
  • validation_skills: final skills run, or skipped with reasons
  • verification: commands, checks, or review evidence summary
  • remaining: top unresolved items or none

For setup-only requests with no execution, return status: configured and exact next commands.

Load References

  • references/xunit.md
  • references/patterns.md
  • references/anti-patterns.md

Example Requests

  • "Run this xUnit suite correctly."
  • "Fix our xUnit v3 test command."
  • "Add an xUnit regression test and keep CI compatible."
Files (dotnet-skills)
  • references
    • anti-patterns.md 15.8 KB
      # xUnit Anti-Patterns
      
      Avoid these common testing mistakes.
      
      ## Test Interdependence
      
      Tests must not depend on execution order or shared mutable state.
      
      Bad:
      
      ```csharp
      public class UserServiceTests
      {
          private static int _testUserId;
      
          [Fact]
          public void CreateUser_ValidData_ReturnsId()
          {
              var service = new UserService();
              _testUserId = service.CreateUser("test@example.com"); // sets static state
              Assert.True(_testUserId > 0);
          }
      
          [Fact]
          public void GetUser_ExistingId_ReturnsUser()
          {
              var service = new UserService();
              var user = service.GetUser(_testUserId); // depends on other test running first
              Assert.NotNull(user);
          }
      }
      ```
      
      Good:
      
      ```csharp
      public class UserServiceTests
      {
          [Fact]
          public void CreateUser_ValidData_ReturnsId()
          {
              // Arrange
              var service = new UserService();
      
              // Act
              var userId = service.CreateUser("test@example.com");
      
              // Assert
              Assert.True(userId > 0);
          }
      
          [Fact]
          public void GetUser_ExistingId_ReturnsUser()
          {
              // Arrange
              var service = new UserService();
              var userId = service.CreateUser("test@example.com"); // each test creates its own data
      
              // Act
              var user = service.GetUser(userId);
      
              // Assert
              Assert.NotNull(user);
          }
      }
      ```
      
      ## Excessive Mocking
      
      Over-mocking creates brittle tests that verify implementation rather than behavior.
      
      Bad:
      
      ```csharp
      public class OrderServiceTests
      {
          [Fact]
          public void PlaceOrder_Valid_CallsAllMethods()
          {
              // Arrange
              var loggerMock = new Mock<ILogger<OrderService>>();
              var repoMock = new Mock<IOrderRepository>();
              var validatorMock = new Mock<IOrderValidator>();
              var pricingMock = new Mock<IPricingService>();
              var inventoryMock = new Mock<IInventoryService>();
              var notificationMock = new Mock<INotificationService>();
      
              validatorMock.Setup(v => v.Validate(It.IsAny<Order>())).Returns(true);
              pricingMock.Setup(p => p.CalculateTotal(It.IsAny<Order>())).Returns(100m);
              inventoryMock.Setup(i => i.Reserve(It.IsAny<Order>())).Returns(true);
              repoMock.Setup(r => r.Save(It.IsAny<Order>())).Returns(1);
      
              var service = new OrderService(loggerMock.Object, repoMock.Object,
                  validatorMock.Object, pricingMock.Object, inventoryMock.Object, notificationMock.Object);
      
              // Act
              service.PlaceOrder(new Order());
      
              // Assert - verifying every internal call creates coupling to implementation
              validatorMock.Verify(v => v.Validate(It.IsAny<Order>()), Times.Once);
              pricingMock.Verify(p => p.CalculateTotal(It.IsAny<Order>()), Times.Once);
              inventoryMock.Verify(i => i.Reserve(It.IsAny<Order>()), Times.Once);
              repoMock.Verify(r => r.Save(It.IsAny<Order>()), Times.Once);
              notificationMock.Verify(n => n.Send(It.IsAny<string>()), Times.Once);
          }
      }
      ```
      
      Good:
      
      ```csharp
      public class OrderServiceTests
      {
          [Fact]
          public void PlaceOrder_ValidOrder_ReturnsOrderId()
          {
              // Arrange - use real implementations where cheap, mock only external boundaries
              var repository = new InMemoryOrderRepository();
              var notificationService = Substitute.For<INotificationService>();
              var service = new OrderService(repository, notificationService);
              var order = new Order { CustomerId = 1, Items = [new OrderItem { ProductId = 1, Quantity = 1 }] };
      
              // Act
              var orderId = service.PlaceOrder(order);
      
              // Assert - verify observable outcomes
              Assert.True(orderId > 0);
              var savedOrder = repository.GetById(orderId);
              Assert.NotNull(savedOrder);
              Assert.Equal(OrderStatus.Placed, savedOrder.Status);
          }
      }
      ```
      
      ## Testing Implementation Instead of Behavior
      
      Tests should verify what the code does, not how it does it.
      
      Bad:
      
      ```csharp
      public class CacheTests
      {
          [Fact]
          public void Get_CacheMiss_CallsUnderlyingStore()
          {
              // Arrange
              var storeMock = new Mock<IDataStore>();
              storeMock.Setup(s => s.Get("key")).Returns("value");
              var cache = new Cache(storeMock.Object);
      
              // Act
              cache.Get("key");
              cache.Get("key");
      
              // Assert - tests internal caching logic, not behavior
              storeMock.Verify(s => s.Get("key"), Times.Once);
          }
      }
      ```
      
      Good:
      
      ```csharp
      public class CacheTests
      {
          [Fact]
          public void Get_SameKey_ReturnsSameValue()
          {
              // Arrange
              var store = new InMemoryDataStore();
              store.Set("key", "value");
              var cache = new Cache(store);
      
              // Act
              var result1 = cache.Get("key");
              var result2 = cache.Get("key");
      
              // Assert - verifies behavior: caching returns consistent results
              Assert.Equal("value", result1);
              Assert.Equal("value", result2);
              Assert.Same(result1, result2); // if reference equality matters
          }
      
          [Fact]
          public void Get_AfterExpiry_ReturnsUpdatedValue()
          {
              // Arrange
              var store = new InMemoryDataStore();
              store.Set("key", "original");
              var timeProvider = new FakeTimeProvider();
              var cache = new Cache(store, timeProvider, ttl: TimeSpan.FromMinutes(5));
      
              // Act
              var result1 = cache.Get("key");
              store.Set("key", "updated");
              timeProvider.Advance(TimeSpan.FromMinutes(10));
              var result2 = cache.Get("key");
      
              // Assert
              Assert.Equal("original", result1);
              Assert.Equal("updated", result2);
          }
      }
      ```
      
      ## Ignoring Test Isolation
      
      Each test must run in isolation without affecting others.
      
      Bad:
      
      ```csharp
      public class ConfigurationTests
      {
          [Fact]
          public void SetGlobalConfig_UpdatesEnvironment()
          {
              Environment.SetEnvironmentVariable("APP_MODE", "test"); // pollutes other tests
              var config = new AppConfiguration();
              Assert.Equal("test", config.Mode);
          }
      }
      ```
      
      Good:
      
      ```csharp
      public class ConfigurationTests : IDisposable
      {
          private readonly string? _originalValue;
      
          public ConfigurationTests()
          {
              _originalValue = Environment.GetEnvironmentVariable("APP_MODE");
          }
      
          [Fact]
          public void SetGlobalConfig_UpdatesEnvironment()
          {
              // Arrange
              Environment.SetEnvironmentVariable("APP_MODE", "test");
              var config = new AppConfiguration();
      
              // Assert
              Assert.Equal("test", config.Mode);
          }
      
          public void Dispose()
          {
              Environment.SetEnvironmentVariable("APP_MODE", _originalValue);
          }
      }
      ```
      
      Even better - inject configuration:
      
      ```csharp
      public class ConfigurationTests
      {
          [Fact]
          public void AppConfiguration_TestMode_ReturnsTestBehavior()
          {
              // Arrange
              var settings = new Dictionary<string, string?> { ["APP_MODE"] = "test" };
              var configuration = new ConfigurationBuilder()
                  .AddInMemoryCollection(settings)
                  .Build();
              var config = new AppConfiguration(configuration);
      
              // Assert
              Assert.Equal("test", config.Mode);
          }
      }
      ```
      
      ## Non-Deterministic Tests
      
      Tests must produce the same result every time.
      
      Bad:
      
      ```csharp
      public class TimestampTests
      {
          [Fact]
          public void CreateRecord_SetsTimestamp()
          {
              // Arrange
              var service = new RecordService();
      
              // Act
              var record = service.CreateRecord("data");
      
              // Assert - fails intermittently when clock rolls over
              Assert.Equal(DateTime.Now.Date, record.CreatedAt.Date);
          }
      }
      
      public class RandomTests
      {
          [Fact]
          public void GenerateId_ReturnsUniqueId()
          {
              var service = new IdGenerator();
              var id1 = service.Generate();
              var id2 = service.Generate();
              Assert.NotEqual(id1, id2); // usually passes, sometimes fails
          }
      }
      ```
      
      Good:
      
      ```csharp
      public class TimestampTests
      {
          [Fact]
          public void CreateRecord_SetsTimestampFromProvider()
          {
              // Arrange
              var fixedTime = new DateTimeOffset(2024, 1, 15, 10, 30, 0, TimeSpan.Zero);
              var timeProvider = new FakeTimeProvider(fixedTime);
              var service = new RecordService(timeProvider);
      
              // Act
              var record = service.CreateRecord("data");
      
              // Assert
              Assert.Equal(fixedTime, record.CreatedAt);
          }
      }
      
      public class RandomTests
      {
          [Fact]
          public void GenerateId_WithSeed_ReturnsExpectedId()
          {
              // Arrange
              var randomProvider = new SeededRandomProvider(42);
              var service = new IdGenerator(randomProvider);
      
              // Act
              var id = service.Generate();
      
              // Assert
              Assert.Equal("expected-seeded-value", id);
          }
      
          [Fact]
          public void GenerateId_ReturnsValidFormat()
          {
              // Arrange
              var service = new IdGenerator();
      
              // Act
              var id = service.Generate();
      
              // Assert - verify format rather than specific value
              Assert.Matches(@"^[a-f0-9]{32}$", id);
          }
      }
      ```
      
      ## Swallowing Exceptions
      
      Tests must not catch exceptions without re-throwing or asserting.
      
      Bad:
      
      ```csharp
      public class FileServiceTests
      {
          [Fact]
          public void ReadFile_MissingFile_HandlesGracefully()
          {
              try
              {
                  var service = new FileService();
                  var content = service.ReadFile("/nonexistent/path");
                  Assert.NotNull(content);
              }
              catch
              {
                  // test passes either way - hides real failures
              }
          }
      }
      ```
      
      Good:
      
      ```csharp
      public class FileServiceTests
      {
          [Fact]
          public void ReadFile_MissingFile_ThrowsFileNotFoundException()
          {
              // Arrange
              var service = new FileService();
      
              // Act & Assert
              var exception = Assert.Throws<FileNotFoundException>(
                  () => service.ReadFile("/nonexistent/path"));
              Assert.Contains("nonexistent", exception.FileName);
          }
      
          [Fact]
          public void TryReadFile_MissingFile_ReturnsFalse()
          {
              // Arrange
              var service = new FileService();
      
              // Act
              var success = service.TryReadFile("/nonexistent/path", out var content);
      
              // Assert
              Assert.False(success);
              Assert.Null(content);
          }
      }
      ```
      
      ## Magic Numbers and Strings
      
      Unexplained literals reduce test clarity.
      
      Bad:
      
      ```csharp
      public class DiscountTests
      {
          [Fact]
          public void CalculateDiscount_ReturnsExpected()
          {
              var service = new DiscountService();
              var discount = service.Calculate(150, 3, true);
              Assert.Equal(22.5m, discount);
          }
      }
      ```
      
      Good:
      
      ```csharp
      public class DiscountTests
      {
          [Fact]
          public void CalculateDiscount_GoldMemberWithBulkOrder_AppliesCombinedDiscount()
          {
              // Arrange
              const decimal orderTotal = 150m;
              const int itemCount = 3;
              const bool isGoldMember = true;
              const decimal expectedBulkDiscount = 15m;      // 10% for 3+ items
              const decimal expectedMemberDiscount = 7.5m;   // 5% gold member bonus
              const decimal expectedTotalDiscount = 22.5m;
      
              var service = new DiscountService();
      
              // Act
              var discount = service.Calculate(orderTotal, itemCount, isGoldMember);
      
              // Assert
              Assert.Equal(expectedTotalDiscount, discount);
          }
      }
      ```
      
      ## Asserting Too Much or Too Little
      
      Each test should verify one logical concept.
      
      Bad - too many assertions:
      
      ```csharp
      public class UserRegistrationTests
      {
          [Fact]
          public void RegisterUser_ValidData_EverythingWorks()
          {
              var service = new UserService();
              var result = service.Register("test@example.com", "password123");
      
              Assert.True(result.Success);
              Assert.NotNull(result.User);
              Assert.Equal("test@example.com", result.User.Email);
              Assert.True(result.User.IsActive);
              Assert.NotNull(result.User.CreatedAt);
              Assert.True(result.EmailSent);
              Assert.Equal(1, service.GetUserCount());
              Assert.NotNull(service.GetUserByEmail("test@example.com"));
              // continues for 20 more assertions
          }
      }
      ```
      
      Bad - too few assertions:
      
      ```csharp
      public class UserRegistrationTests
      {
          [Fact]
          public void RegisterUser_ValidData_Succeeds()
          {
              var service = new UserService();
              var result = service.Register("test@example.com", "password123");
              Assert.True(result.Success); // what about the user? was it actually created?
          }
      }
      ```
      
      Good:
      
      ```csharp
      public class UserRegistrationTests
      {
          [Fact]
          public void RegisterUser_ValidData_CreatesActiveUser()
          {
              // Arrange
              var service = new UserService();
              const string email = "test@example.com";
      
              // Act
              var result = service.Register(email, "password123");
      
              // Assert
              Assert.True(result.Success);
              Assert.NotNull(result.User);
              Assert.Equal(email, result.User.Email);
              Assert.True(result.User.IsActive);
          }
      
          [Fact]
          public void RegisterUser_ValidData_SendsWelcomeEmail()
          {
              // Arrange
              var emailService = Substitute.For<IEmailService>();
              var service = new UserService(emailService);
      
              // Act
              service.Register("test@example.com", "password123");
      
              // Assert
              emailService.Received(1).SendWelcomeEmail("test@example.com");
          }
      
          [Fact]
          public void RegisterUser_ValidData_PersistsUser()
          {
              // Arrange
              var repository = new InMemoryUserRepository();
              var service = new UserService(repository);
      
              // Act
              var result = service.Register("test@example.com", "password123");
      
              // Assert
              var persistedUser = repository.GetByEmail("test@example.com");
              Assert.NotNull(persistedUser);
              Assert.Equal(result.User.Id, persistedUser.Id);
          }
      }
      ```
      
      ## Async Void Tests
      
      xUnit does not properly await `async void` test methods.
      
      Bad:
      
      ```csharp
      public class AsyncTests
      {
          [Fact]
          public async void FetchData_ReturnsData() // async void - xUnit won't wait
          {
              var service = new DataService();
              var data = await service.FetchAsync();
              Assert.NotEmpty(data); // may not execute before test completes
          }
      }
      ```
      
      Good:
      
      ```csharp
      public class AsyncTests
      {
          [Fact]
          public async Task FetchData_ReturnsData() // async Task - xUnit awaits properly
          {
              // Arrange
              var service = new DataService();
      
              // Act
              var data = await service.FetchAsync();
      
              // Assert
              Assert.NotEmpty(data);
          }
      }
      ```
      
      ## Constructor Abuse
      
      Test constructors are for shared setup, not test-specific logic.
      
      Bad:
      
      ```csharp
      public class PaymentTests
      {
          private readonly PaymentResult _result;
      
          public PaymentTests()
          {
              var service = new PaymentService();
              _result = service.ProcessPayment(new Payment { Amount = 100 }); // runs for every test
          }
      
          [Fact]
          public void ProcessPayment_ValidAmount_Succeeds() => Assert.True(_result.Success);
      
          [Fact]
          public void ProcessPayment_ValidAmount_ReturnsTransactionId() => Assert.NotNull(_result.TransactionId);
      }
      ```
      
      Good:
      
      ```csharp
      public class PaymentTests(PaymentFixture fixture) : IClassFixture<PaymentFixture>
      {
          [Fact]
          public void ProcessPayment_ValidAmount_Succeeds()
          {
              // Arrange
              var service = new PaymentService(fixture.Gateway);
              var payment = new Payment { Amount = 100 };
      
              // Act
              var result = service.ProcessPayment(payment);
      
              // Assert
              Assert.True(result.Success);
          }
      
          [Fact]
          public void ProcessPayment_ValidAmount_ReturnsTransactionId()
          {
              // Arrange
              var service = new PaymentService(fixture.Gateway);
              var payment = new Payment { Amount = 100 };
      
              // Act
              var result = service.ProcessPayment(payment);
      
              // Assert
              Assert.NotNull(result.TransactionId);
          }
      }
      
      public class PaymentFixture
      {
          public IPaymentGateway Gateway { get; } = new TestPaymentGateway();
      }
      ```
      
      ## Sources
      
      - [xUnit.net Patterns](https://xunit.net/docs/comparisons)
      - [Unit Testing Best Practices](https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices)
      
    • patterns.md 9.7 KB
      # xUnit Testing Patterns
      
      ## Arrange-Act-Assert (AAA)
      
      Structure every test method in three distinct phases:
      
      ```csharp
      public class CalculatorTests
      {
          [Fact]
          public void Add_TwoPositiveNumbers_ReturnsSum()
          {
              // Arrange
              var calculator = new Calculator();
              var a = 5;
              var b = 3;
      
              // Act
              var result = calculator.Add(a, b);
      
              // Assert
              Assert.Equal(8, result);
          }
      }
      ```
      
      Keep the phases visually separated. Avoid mixing setup, execution, and verification in the same block.
      
      ## Class Fixtures
      
      Use class fixtures to share expensive setup across all tests in a class. Primary constructors inject the fixture directly:
      
      ```csharp
      public class DatabaseFixture : IAsyncLifetime
      {
          public IDbConnection Connection { get; private set; } = null!;
      
          public async Task InitializeAsync()
          {
              Connection = new SqliteConnection("Data Source=:memory:");
              await Connection.OpenAsync();
              await SeedTestDataAsync();
          }
      
          public async Task DisposeAsync()
          {
              await Connection.DisposeAsync();
          }
      
          private async Task SeedTestDataAsync()
          {
              // seed shared test data
          }
      }
      
      public class UserRepositoryTests(DatabaseFixture fixture) : IClassFixture<DatabaseFixture>
      {
          [Fact]
          public async Task GetUser_ExistingId_ReturnsUser()
          {
              // Arrange
              var repository = new UserRepository(fixture.Connection);
      
              // Act
              var user = await repository.GetUserAsync(1);
      
              // Assert
              Assert.NotNull(user);
              Assert.Equal("TestUser", user.Name);
          }
      }
      ```
      
      ## Collection Fixtures
      
      Share expensive resources across multiple test classes:
      
      ```csharp
      public class IntegrationTestFixture : IAsyncLifetime
      {
          public HttpClient Client { get; private set; } = null!;
          private WebApplicationFactory<Program> _factory = null!;
      
          public async Task InitializeAsync()
          {
              _factory = new WebApplicationFactory<Program>();
              Client = _factory.CreateClient();
              await Task.CompletedTask;
          }
      
          public async Task DisposeAsync()
          {
              Client.Dispose();
              await _factory.DisposeAsync();
          }
      }
      
      [CollectionDefinition("Integration")]
      public class IntegrationTestCollection : ICollectionFixture<IntegrationTestFixture>;
      
      [Collection("Integration")]
      public class OrdersApiTests(IntegrationTestFixture fixture)
      {
          [Fact]
          public async Task GetOrders_ReturnsOk()
          {
              // Act
              var response = await fixture.Client.GetAsync("/api/orders");
      
              // Assert
              response.EnsureSuccessStatusCode();
          }
      }
      
      [Collection("Integration")]
      public class ProductsApiTests(IntegrationTestFixture fixture)
      {
          [Fact]
          public async Task GetProducts_ReturnsOk()
          {
              // Act
              var response = await fixture.Client.GetAsync("/api/products");
      
              // Assert
              response.EnsureSuccessStatusCode();
          }
      }
      ```
      
      ## Theory Data Patterns
      
      ### InlineData for Simple Cases
      
      ```csharp
      public class ValidationTests
      {
          [Theory]
          [InlineData("", false)]
          [InlineData("a", false)]
          [InlineData("ab", false)]
          [InlineData("abc", true)]
          [InlineData("valid-password-123", true)]
          public void IsValidPassword_VariousInputs_ReturnsExpected(string password, bool expected)
          {
              // Arrange
              var validator = new PasswordValidator(minLength: 3);
      
              // Act
              var result = validator.IsValid(password);
      
              // Assert
              Assert.Equal(expected, result);
          }
      }
      ```
      
      ### MemberData for Complex Objects
      
      ```csharp
      public class OrderProcessorTests
      {
          public static TheoryData<Order, decimal> OrderDiscountData => new()
          {
              { new Order { Total = 100m, CustomerTier = CustomerTier.Standard }, 0m },
              { new Order { Total = 100m, CustomerTier = CustomerTier.Silver }, 5m },
              { new Order { Total = 100m, CustomerTier = CustomerTier.Gold }, 10m },
              { new Order { Total = 500m, CustomerTier = CustomerTier.Gold }, 75m }
          };
      
          [Theory]
          [MemberData(nameof(OrderDiscountData))]
          public void CalculateDiscount_VariousOrders_ReturnsExpectedDiscount(Order order, decimal expectedDiscount)
          {
              // Arrange
              var processor = new OrderProcessor();
      
              // Act
              var discount = processor.CalculateDiscount(order);
      
              // Assert
              Assert.Equal(expectedDiscount, discount);
          }
      }
      ```
      
      ### ClassData for Reusable Data Sets
      
      ```csharp
      public class EdgeCaseStringData : TheoryData<string?>
      {
          public EdgeCaseStringData()
          {
              Add(null);
              Add("");
              Add(" ");
              Add("\t");
              Add("\n");
              Add("   \t\n   ");
          }
      }
      
      public class StringUtilityTests
      {
          [Theory]
          [ClassData(typeof(EdgeCaseStringData))]
          public void IsNullOrWhitespace_EdgeCases_ReturnsTrue(string? input)
          {
              // Act
              var result = string.IsNullOrWhiteSpace(input);
      
              // Assert
              Assert.True(result);
          }
      }
      ```
      
      ## Mocking with NSubstitute
      
      Prefer NSubstitute for readable substitute configuration:
      
      ```csharp
      public class OrderServiceTests(ITestOutputHelper output)
      {
          [Fact]
          public async Task PlaceOrder_ValidOrder_SendsNotification()
          {
              // Arrange
              var notificationService = Substitute.For<INotificationService>();
              var orderRepository = Substitute.For<IOrderRepository>();
              orderRepository.SaveAsync(Arg.Any<Order>()).Returns(Task.FromResult(true));
      
              var service = new OrderService(orderRepository, notificationService);
              var order = new Order { CustomerId = 1, Items = [new OrderItem { ProductId = 1, Quantity = 2 }] };
      
              // Act
              await service.PlaceOrderAsync(order);
      
              // Assert
              await notificationService.Received(1).SendOrderConfirmationAsync(Arg.Is<Order>(o => o.CustomerId == 1));
              output.WriteLine("Order placed and notification sent");
          }
      
          [Fact]
          public async Task PlaceOrder_RepositoryFails_ThrowsException()
          {
              // Arrange
              var notificationService = Substitute.For<INotificationService>();
              var orderRepository = Substitute.For<IOrderRepository>();
              orderRepository.SaveAsync(Arg.Any<Order>()).ThrowsAsync(new DataException("Connection failed"));
      
              var service = new OrderService(orderRepository, notificationService);
              var order = new Order { CustomerId = 1 };
      
              // Act & Assert
              await Assert.ThrowsAsync<DataException>(() => service.PlaceOrderAsync(order));
              await notificationService.DidNotReceive().SendOrderConfirmationAsync(Arg.Any<Order>());
          }
      }
      ```
      
      ## Mocking with Moq
      
      Use Moq when the project already depends on it:
      
      ```csharp
      public class PaymentProcessorTests
      {
          [Fact]
          public async Task ProcessPayment_ValidCard_ReturnsSuccess()
          {
              // Arrange
              var gatewayMock = new Mock<IPaymentGateway>();
              gatewayMock
                  .Setup(g => g.ChargeAsync(It.IsAny<string>(), It.Is<decimal>(d => d > 0)))
                  .ReturnsAsync(new PaymentResult { Success = true, TransactionId = "TX123" });
      
              var processor = new PaymentProcessor(gatewayMock.Object);
      
              // Act
              var result = await processor.ProcessPaymentAsync("4111111111111111", 99.99m);
      
              // Assert
              Assert.True(result.Success);
              Assert.Equal("TX123", result.TransactionId);
              gatewayMock.Verify(g => g.ChargeAsync("4111111111111111", 99.99m), Times.Once);
          }
      }
      ```
      
      ## Output and Diagnostics
      
      Use `ITestOutputHelper` for test diagnostics:
      
      ```csharp
      public class DiagnosticTests(ITestOutputHelper output)
      {
          [Fact]
          public void ComplexCalculation_LargeInput_CompletesWithinTimeout()
          {
              // Arrange
              var calculator = new ComplexCalculator();
              var input = GenerateLargeInput();
              output.WriteLine($"Testing with input size: {input.Length}");
      
              var stopwatch = Stopwatch.StartNew();
      
              // Act
              var result = calculator.Process(input);
      
              // Assert
              stopwatch.Stop();
              output.WriteLine($"Completed in {stopwatch.ElapsedMilliseconds}ms");
              Assert.True(stopwatch.ElapsedMilliseconds < 5000, "Calculation took too long");
          }
      
          private static int[] GenerateLargeInput() => Enumerable.Range(0, 100_000).ToArray();
      }
      ```
      
      ## Async Test Patterns
      
      xUnit handles async tests natively:
      
      ```csharp
      public class AsyncServiceTests
      {
          [Fact]
          public async Task FetchData_ValidEndpoint_ReturnsData()
          {
              // Arrange
              var service = new DataService();
      
              // Act
              var data = await service.FetchDataAsync("/api/items");
      
              // Assert
              Assert.NotEmpty(data);
          }
      
          [Fact]
          public async Task FetchData_Timeout_ThrowsOperationCanceledException()
          {
              // Arrange
              var service = new DataService();
              using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(1));
      
              // Act & Assert
              await Assert.ThrowsAsync<OperationCanceledException>(
                  () => service.FetchDataAsync("/api/slow-endpoint", cts.Token));
          }
      }
      ```
      
      ## Trait-Based Organization
      
      Use traits sparingly for CI filtering:
      
      ```csharp
      public class IntegrationTests
      {
          [Fact]
          [Trait("Category", "Integration")]
          [Trait("Database", "SqlServer")]
          public async Task CreateUser_ValidData_PersistsToDatabase()
          {
              // integration test implementation
          }
      }
      
      public class UnitTests
      {
          [Fact]
          [Trait("Category", "Unit")]
          public void ValidateEmail_InvalidFormat_ReturnsFalse()
          {
              // unit test implementation
          }
      }
      ```
      
      Run filtered:
      
      ```bash
      dotnet test --filter "Category=Unit"
      dotnet test --filter "Category!=Integration"
      ```
      
      ## Sources
      
      - [xUnit.net Theory Data](https://xunit.net/docs/getting-started/v3/theory-data)
      - [xUnit.net Shared Context](https://xunit.net/docs/shared-context)
      - [NSubstitute Documentation](https://nsubstitute.github.io/help/getting-started/)
      
    • xunit.md 3.1 KB
      # xUnit in MCAF
      
      ## Open/Free Status
      
      - open source
      - free to use
      
      ## Install
      
      xUnit v3 package setup:
      
      ```bash
      dotnet add package xunit.v3
      ```
      
      VSTest compatibility package when the repo intentionally uses that runner:
      
      ```bash
      dotnet add package xunit.runner.visualstudio
      ```
      
      ## Verify First
      
      Before adding packages, check what the repo already references:
      
      ```bash
      rg -n "xunit(\\.v3)?|xunit\\.runner\\.visualstudio|TestingPlatformDotnetTestSupport|UseMicrosoftTestingPlatformRunner" -g '*.csproj' .
      ```
      
      Use this reference when the repository already chose xUnit and you need framework-specific commands, package checks, or CI guardrails.
      
      ## Detect the xUnit Model
      
      Use the project file as the source of truth:
      
      ```bash
      rg -n "xunit\\.v3|xunit.runner.visualstudio|Microsoft\\.NET\\.Test\\.Sdk|TestingPlatformDotnetTestSupport|UseMicrosoftTestingPlatformRunner" -g '*.csproj' .
      ```
      
      Typical markers:
      
      - xUnit v2: `xunit`
      - xUnit v3: `xunit.v3`
      - VSTest compatibility: `xunit.runner.visualstudio` and `Microsoft.NET.Test.Sdk`
      - Microsoft.Testing.Platform support: `TestingPlatformDotnetTestSupport` or `UseMicrosoftTestingPlatformRunner`
      
      ## Common Commands
      
      Start with the repo's `test` command from `AGENTS.md`. If the repo has not documented one yet, these are the safe defaults:
      
      ```bash
      dotnet test MySolution.sln
      dotnet test tests/MyProject.Tests/MyProject.Tests.csproj
      dotnet test tests/MyProject.Tests/MyProject.Tests.csproj --no-build
      ```
      
      Focused VSTest-style run:
      
      ```bash
      dotnet test tests/MyProject.Tests/MyProject.Tests.csproj --filter "FullyQualifiedName~Namespace.TypeName"
      ```
      
      xUnit v3 standalone runner:
      
      ```bash
      dotnet run --project tests/MyProject.Tests/MyProject.Tests.csproj
      ```
      
      xUnit v3 with Microsoft.Testing.Platform-style class filtering:
      
      ```bash
      dotnet run --project tests/MyProject.Tests/MyProject.Tests.csproj -- --filter-class Namespace.TypeName
      ```
      
      If the project enables `TestingPlatformDotnetTestSupport`, `dotnet test` can forward into Microsoft.Testing.Platform. Keep those switches consistent with the runner the project actually uses.
      
      ## CI Notes
      
      - Use one runner model per project. Do not mix VSTest-only flags and Microsoft.Testing.Platform flags in the same command.
      - Build first, then use `--no-build` for repeat test runs.
      - Keep coverage driver aligned with the runner:
        - VSTest: `coverlet.collector` or `--collect:"XPlat Code Coverage"`
        - Microsoft.Testing.Platform: `coverlet.MTP`
      - Keep xUnit analyzers on:
        - xUnit v2 2.3+ usually brings them through the main `xunit` package
        - xUnit v3 brings analyzer guidance through the main package set unless the repo split packages explicitly
      
      ## Good Defaults
      
      - prefer `[Theory]` plus stable inline or member data for variant-heavy behavior
      - use traits only when the repo already relies on them for filtering
      - avoid runner rewrites in the same change as behavior work unless the current command is already broken
      
      ## Sources
      
      - [xUnit.net v3 getting started](https://xunit.net/docs/getting-started/v3/getting-started)
      - [xUnit.net v3 Microsoft Testing Platform support](https://xunit.net/docs/getting-started/v3/microsoft-testing-platform)
      - [xunit/xunit.analyzers](https://github.com/xunit/xunit.analyzers)
      
  • SKILL.md 5.1 KB
    ---
    name: dotnet-xunit
    version: "1.0.0"
    category: "Testing"
    description: "Write, run, or repair .NET tests that use xUnit. Use when a repo uses `xunit`, `xunit.v3`, `[Fact]`, `[Theory]`, or `xunit.runner.visualstudio`, and you need the right CLI, package, and runner guidance for xUnit on VSTest or Microsoft.Testing.Platform."
    compatibility: "Requires a .NET solution or project with xUnit packages; respects the repo's `AGENTS.md` commands first."
    ---
    
    # xUnit.net
    
    ## Trigger On
    
    - the repo uses xUnit v2 or xUnit v3
    - you need to add, run, debug, or repair xUnit tests
    - the team is unsure whether a project is using VSTest or Microsoft.Testing.Platform
    
    ## Value
    
    - produce a concrete project delta: code, docs, config, tests, CI, or review artifact
    - reduce ambiguity through explicit planning, verification, and final validation skills
    - leave reusable project context so future tasks are faster and safer
    
    ## Do Not Use For
    
    - TUnit projects
    - MSTest projects
    - generic test strategy with no xUnit-specific mechanics
    
    ## Inputs
    
    - the nearest `AGENTS.md`
    - the test project file and package references
    - the active runner model for the test project
    
    ## Quick Start
    
    1. Read the nearest `AGENTS.md` and confirm scope and constraints.
    2. Run this skill's `Workflow` through the `Ralph Loop` until outcomes are acceptable.
    3. Return the `Required Result Format` with concrete artifacts and verification evidence.
    
    ## Workflow
    
    1. Detect the active xUnit model before changing commands:
       - `xunit` usually means v2
       - `xunit.v3` means v3
       - `xunit.runner.visualstudio` plus `Microsoft.NET.Test.Sdk` usually means VSTest compatibility is enabled
       - `TestingPlatformDotnetTestSupport` or `UseMicrosoftTestingPlatformRunner` means Microsoft.Testing.Platform is in play
    2. Read the repo's real `test` command from `AGENTS.md`. If the repo has no explicit command yet, start with `dotnet test PROJECT_OR_SOLUTION`.
    3. Keep the runner model consistent:
       - xUnit v2 usually runs through VSTest
       - xUnit v3 can run as a standalone executable with `dotnet run`
       - xUnit v3 can also integrate with Microsoft.Testing.Platform
       - do not mix VSTest-only switches into Microsoft.Testing.Platform runs
    4. Run the narrowest useful scope first:
       - one project
       - one class
       - one trait
       - one method
    5. Prefer `[Theory]` for stable data-driven coverage and `[Fact]` for single-path invariant checks.
    6. Keep `xunit.analyzers` enabled when present. Fix analyzer findings instead of muting them casually.
    
    ## Bootstrap When Missing
    
    If xUnit is requested but not configured:
    
    1. Detect current framework first:
       - `rg -n "xunit(\\.v3)?|xunit\\.runner\\.visualstudio|TestingPlatformDotnetTestSupport|UseMicrosoftTestingPlatformRunner|TUnit|MSTest" -g '*.csproj' .`
    2. If the repo currently uses `TUnit` or `MSTest`, do not auto-migrate. Return `status: not_applicable` unless migration is explicitly requested.
    3. For explicit xUnit adoption, add packages to the target test project:
       - `dotnet add TEST_PROJECT.csproj package xunit.v3`
       - optional VSTest bridge: `dotnet add TEST_PROJECT.csproj package xunit.runner.visualstudio`
    4. Add repo test commands and runner notes to `AGENTS.md`.
    5. Run `dotnet test TEST_PROJECT.csproj` or repo-defined xUnit command and return `status: configured` or `status: improved`.
    
    
    ## Deliver
    
    - xUnit tests that match the repo's active xUnit version and runner
    - commands that work in local and CI runs
    - focused verification before broader suite execution
    
    ## Validate
    
    - the chosen CLI matches the active runner model
    - test filters or focused runs are valid for that runner
    - tests use deterministic inputs and assertions
    - xUnit-specific analyzers remain active unless the repo documents an exception
    
    ## Ralph Loop
    
    Use the Ralph Loop for every task, including docs, architecture, testing, and tooling work.
    
    1. Plan first (mandatory):
       - analyze current state
       - define target outcome, constraints, and risks
       - write a detailed execution plan
       - list final validation skills to run at the end, with order and reason
    2. Execute one planned step and produce a concrete delta.
    3. Review the result and capture findings with actionable next fixes.
    4. Apply fixes in small batches and rerun the relevant checks or review steps.
    5. Update the plan after each iteration.
    6. Repeat until outcomes are acceptable or only explicit exceptions remain.
    7. If a dependency is missing, bootstrap it or return `status: not_applicable` with explicit reason and fallback path.
    
    ### Required Result Format
    
    - `status`: `complete` | `clean` | `improved` | `configured` | `not_applicable` | `blocked`
    - `plan`: concise plan and current iteration step
    - `actions_taken`: concrete changes made
    - `validation_skills`: final skills run, or skipped with reasons
    - `verification`: commands, checks, or review evidence summary
    - `remaining`: top unresolved items or `none`
    
    For setup-only requests with no execution, return `status: configured` and exact next commands.
    
    ## Load References
    
    - `references/xunit.md`
    - `references/patterns.md`
    - `references/anti-patterns.md`
    
    ## Example Requests
    
    - "Run this xUnit suite correctly."
    - "Fix our xUnit v3 test command."
    - "Add an xUnit regression test and keep CI compatible."
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related