[{"data":1,"prerenderedAt":106},["ShallowReactive",2],{"qa-\u002Fdotnet\u002Ftesting\u002Funit-testing":3},{"page":4,"siblings":94,"blog":103},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":85,"related":86,"seo":87,"seoDescription":88,"stem":89,"subtopic":6,"topic":90,"topicSlug":91,"updated":92,"__hash__":93},"qa\u002Fdotnet\u002Ftesting\u002Funit-testing.md","Unit Testing",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md",".NET Core","dotnet",{},true,1,"\u002Fdotnet\u002Ftesting\u002Funit-testing",[23,28,32,36,40,44,48,52,56,60,64,68,72,77,81],{"id":24,"difficulty":25,"q":26,"a":27},"unit-test-definition","easy","What is a unit test and what makes a good unit test in .NET?","A **unit test** verifies a single unit of behavior (a method or class) in\ncomplete isolation from external dependencies. Good unit tests follow the\n**FIRST** properties: Fast, Isolated, Repeatable, Self-validating, Timely.\n\n```csharp\n\u002F\u002F xUnit example — testing a pure calculation with no external deps:\npublic class PriceCalculatorTests\n{\n    [Fact]\n    public void CalculateDiscount_WhenOver100_Returns10PercentOff()\n    {\n        \u002F\u002F Arrange — set up inputs\n        var calculator = new PriceCalculator();\n\n        \u002F\u002F Act — exercise the unit\n        var result = calculator.CalculateDiscount(totalPrice: 150m);\n\n        \u002F\u002F Assert — verify one logical outcome\n        Assert.Equal(135m, result);\n    }\n\n    \u002F\u002F Bad test — asserts multiple unrelated outcomes in one test\n    \u002F\u002F Makes failures ambiguous; keep each test focused on one behaviour\n    [Fact]\n    public void BadTest_TooManyAsserts()\n    {\n        var c = new PriceCalculator();\n        Assert.Equal(135m, c.CalculateDiscount(150m)); \u002F\u002F 10% off\n        Assert.Equal(100m, c.CalculateDiscount(100m)); \u002F\u002F no discount\n        Assert.Equal(0m,   c.CalculateDiscount(0m));   \u002F\u002F zero\n    }\n}\n```\n\nA unit test should fail for exactly one reason. If it can fail for multiple\nreasons, split it into multiple tests.\n\n**Rule of thumb:** A unit test that touches the database, filesystem, or network\nis an integration test in disguise — extract the dependency and mock it instead.\n",{"id":29,"difficulty":25,"q":30,"a":31},"xunit-vs-nunit-vs-mstest","What are the differences between xUnit, NUnit, and MSTest?","All three are supported by the .NET test runner, but they differ in philosophy\nand syntax. **xUnit** is the modern default for new .NET projects. **NUnit** is\nolder but feature-rich. **MSTest** ships with Visual Studio and is Microsoft's\nfirst-party framework.\n\n```csharp\n\u002F\u002F Test marker attributes — same idea, different names:\n\n\u002F\u002F xUnit\n[Fact]            \u002F\u002F single test\n[Theory]          \u002F\u002F data-driven test\n[InlineData(1,2)] \u002F\u002F inline dataset\n\n\u002F\u002F NUnit\n[Test]\n[TestCase(1, 2)]\n[TestFixture]     \u002F\u002F marks the test class\n\n\u002F\u002F MSTest\n[TestMethod]\n[DataTestMethod]\n[DataRow(1, 2)]\n[TestClass]       \u002F\u002F marks the test class\n\n\u002F\u002F xUnit creates a new instance per test (isolation by design):\npublic class XUnitTests\n{\n    private readonly List\u003Cint> _items = new(); \u002F\u002F fresh per test\n    [Fact] public void Test1() => _items.Add(1);\n    [Fact] public void Test2() => _items.Add(2); \u002F\u002F _items is always empty here\n}\n\n\u002F\u002F NUnit\u002FMSTest reuse the same instance — use [SetUp]\u002F[TestInitialize] to reset:\n[TestFixture]\npublic class NUnitTests\n{\n    private List\u003Cint> _items;\n    [SetUp] public void Setup() => _items = new List\u003Cint>(); \u002F\u002F reset each time\n}\n```\n\nKey xUnit differentiator: no `[SetUp]`\u002F`[TearDown]` — use constructor for setup\nand `IDisposable` for teardown, which makes the lifecycle explicit.\n\n**Rule of thumb:** Choose xUnit for new projects; it enforces better isolation\npatterns by design. Match the framework your team already uses if joining existing code.\n",{"id":33,"difficulty":25,"q":34,"a":35},"arrange-act-assert","What is the Arrange-Act-Assert pattern and why does it matter?","**Arrange-Act-Assert (AAA)** is a structural convention for writing tests clearly.\nIt separates setup, execution, and verification into three distinct phases, making\nthe test's intent immediately readable.\n\n```csharp\npublic class OrderServiceTests\n{\n    [Fact]\n    public void PlaceOrder_WhenStockAvailable_ReturnsConfirmedOrder()\n    {\n        \u002F\u002F Arrange — prepare everything the test needs\n        var mockInventory = new Mock\u003CIInventoryService>();\n        mockInventory\n            .Setup(i => i.IsInStock(\"SKU-42\", quantity: 2))\n            .Returns(true);\n\n        var service = new OrderService(mockInventory.Object);\n        var order   = new Order { Sku = \"SKU-42\", Quantity = 2 };\n\n        \u002F\u002F Act — call exactly ONE thing being tested\n        var result = service.PlaceOrder(order);\n\n        \u002F\u002F Assert — verify the expected outcome\n        Assert.Equal(OrderStatus.Confirmed, result.Status);\n    }\n}\n\n\u002F\u002F Common mistake: Arrange inside Assert section\n\u002F\u002F [Fact]\n\u002F\u002F public void BadLayout()\n\u002F\u002F {\n\u002F\u002F var service = new OrderService(); \u002F\u002F scattered setup\n\u002F\u002F var result  = service.PlaceOrder(new Order { Sku = \"X\" });\n\u002F\u002F var expected = OrderStatus.Confirmed; \u002F\u002F computed in assert block\n\u002F\u002F Assert.Equal(expected, result.Status);\n\u002F\u002F }\n```\n\nWhen a test fails, the three-section structure tells you immediately whether\nsetup failed (Arrange), the wrong method was called (Act), or the result\nwas wrong (Assert).\n\n**Rule of thumb:** If your Act section has more than one line, you are probably\ntesting two things — split it.\n",{"id":37,"difficulty":25,"q":38,"a":39},"test-naming-conventions","What naming conventions are used for unit tests in .NET?","Test names should communicate what is being tested, under what condition, and\nwhat the expected outcome is — without needing to read the body.\n\n```csharp\n\u002F\u002F Pattern 1 — MethodName_Scenario_ExpectedResult (most common):\npublic void CalculateDiscount_WhenOrderOver100_Returns10Percent()\npublic void Login_WithInvalidPassword_ThrowsUnauthorizedException()\npublic void GetUser_WhenUserDoesNotExist_ReturnsNull()\n\n\u002F\u002F Pattern 2 — Given_When_Then (BDD style, popular with NUnit\u002FGherkin):\npublic void Given_OrderOver100_When_DiscountCalculated_Then_Returns10Percent()\n\n\u002F\u002F Pattern 3 — natural language (xUnit with DisplayName):\n[Fact(DisplayName = \"Discount is 10% when order total exceeds $100\")]\npublic void Discount_Applied_For_Large_Orders() { }\n\n\u002F\u002F Test class naming — mirrors the class under test:\npublic class PriceCalculatorTests { }   \u002F\u002F testing PriceCalculator\npublic class OrderServiceTests { }      \u002F\u002F testing OrderService\n\n\u002F\u002F File placement — same namespace structure in a separate test project:\n\u002F\u002F src\u002FMyApp\u002FServices\u002FOrderService.cs\n\u002F\u002F tests\u002FMyApp.Tests\u002FServices\u002FOrderServiceTests.cs\n```\n\nA well-named test acts as living documentation: the test report should read like\na specification of the system's expected behavior.\n\n**Rule of thumb:** If you need to read the test body to understand what it tests,\nthe name is not descriptive enough.\n",{"id":41,"difficulty":14,"q":42,"a":43},"data-driven-tests","How do you write data-driven (parameterised) tests in xUnit?","xUnit uses `[Theory]` with `[InlineData]`, `[MemberData]`, or `[ClassData]`\nto run the same test logic against multiple inputs without code duplication.\n\n```csharp\npublic class DiscountCalculatorTests\n{\n    \u002F\u002F [InlineData] — simplest: literals in the attribute\n    [Theory]\n    [InlineData(50,  50)]   \u002F\u002F below threshold → no discount\n    [InlineData(100, 100)]  \u002F\u002F exactly at threshold → no discount\n    [InlineData(150, 135)]  \u002F\u002F above threshold → 10% off\n    [InlineData(200, 180)]\n    public void CalculateDiscount_ReturnsExpectedPrice(\n        decimal input, decimal expected)\n    {\n        var calc   = new DiscountCalculator();\n        var result = calc.Calculate(input);\n        Assert.Equal(expected, result);\n    }\n\n    \u002F\u002F [MemberData] — use when datasets are complex or reused\n    public static IEnumerable\u003Cobject[]> InvalidOrders => new[]\n    {\n        new object[] { null,                   \"order\" },\n        new object[] { new Order { Qty = 0 },  \"qty\"   },\n        new object[] { new Order { Sku = \"\" },  \"sku\"  },\n    };\n\n    [Theory]\n    [MemberData(nameof(InvalidOrders))]\n    public void PlaceOrder_WithInvalidInput_ThrowsArgumentException(\n        Order order, string paramName)\n    {\n        var svc = new OrderService();\n        var ex  = Assert.Throws\u003CArgumentException>(() => svc.PlaceOrder(order));\n        Assert.Contains(paramName, ex.ParamName);\n    }\n}\n```\n\n`[ClassData]` is for when the dataset logic belongs in its own class (useful\nfor complex generation or when the data source is shared across test classes).\n\n**Rule of thumb:** Use `[InlineData]` for simple literals, `[MemberData]` when\nthe dataset is large or shared, and `[ClassData]` when the dataset needs its\nown setup logic.\n",{"id":45,"difficulty":14,"q":46,"a":47},"test-isolation","How do you achieve test isolation in .NET unit tests?","Test isolation means each test runs independently — no shared mutable state,\nno ordering dependency, no side effects that bleed between tests.\n\n```csharp\n\u002F\u002F Problem: shared static state breaks isolation\npublic class BadTests\n{\n    private static List\u003Cstring> _log = new(); \u002F\u002F shared across all tests!\n\n    [Fact] public void Test1() { _log.Add(\"a\"); Assert.Single(_log); }\n    [Fact] public void Test2() { _log.Add(\"b\"); Assert.Single(_log); } \u002F\u002F may fail!\n}\n\n\u002F\u002F Fix 1: xUnit creates a new instance per test — use instance fields\npublic class GoodTests\n{\n    private readonly List\u003Cstring> _log = new(); \u002F\u002F fresh per test instance\n\n    [Fact] public void Test1() { _log.Add(\"a\"); Assert.Single(_log); }\n    [Fact] public void Test2() { _log.Add(\"b\"); Assert.Single(_log); }\n}\n\n\u002F\u002F Fix 2: shared expensive resources use IClassFixture (created once, shared read-only)\npublic class DatabaseFixture : IDisposable\n{\n    public SqliteConnection Db { get; } = new(\"Data Source=:memory:\");\n    public DatabaseFixture() { Db.Open(); \u002F* seed schema *\u002F }\n    public void Dispose()    { Db.Close(); }\n}\n\n\u002F\u002F IClassFixture\u003CT> — one fixture instance shared across all tests in the class\npublic class QueryTests : IClassFixture\u003CDatabaseFixture>\n{\n    private readonly DatabaseFixture _fixture;\n    public QueryTests(DatabaseFixture fixture) => _fixture = fixture;\n\n    [Fact] public void Query_ReturnsResults() { \u002F* uses _fixture.Db *\u002F }\n}\n```\n\nFor shared state across multiple test classes, use `ICollectionFixture\u003CT>`\nwith `[Collection(\"name\")]`.\n\n**Rule of thumb:** Mutate instance fields freely — xUnit's per-instance model\nkeeps them isolated. Reserve `IClassFixture` only for expensive setup (in-memory\ndatabases, servers) that is too slow to rebuild per test.\n",{"id":49,"difficulty":14,"q":50,"a":51},"testing-exceptions","How do you test that a method throws the correct exception in xUnit?","xUnit provides `Assert.Throws\u003CT>` and `Assert.ThrowsAsync\u003CT>` to verify that\na specific exception type is thrown. You can also inspect the exception instance.\n\n```csharp\npublic class OrderServiceTests\n{\n    [Fact]\n    public void PlaceOrder_WithNullOrder_ThrowsArgumentNullException()\n    {\n        var service = new OrderService();\n\n        \u002F\u002F Assert.Throws returns the exception so you can inspect it\n        var ex = Assert.Throws\u003CArgumentNullException>(\n            () => service.PlaceOrder(null));\n\n        Assert.Equal(\"order\", ex.ParamName); \u002F\u002F verify the parameter name\n    }\n\n    [Fact]\n    public async Task PlaceOrderAsync_WhenOutOfStock_ThrowsInvalidOperationException()\n    {\n        var mockInventory = new Mock\u003CIInventoryService>();\n        mockInventory.Setup(i => i.IsInStock(It.IsAny\u003Cstring>(), It.IsAny\u003Cint>()))\n                     .ReturnsAsync(false);\n\n        var service = new OrderService(mockInventory.Object);\n\n        \u002F\u002F Async exception testing:\n        var ex = await Assert.ThrowsAsync\u003CInvalidOperationException>(\n            () => service.PlaceOrderAsync(new Order { Sku = \"X\", Quantity = 1 }));\n\n        Assert.Contains(\"out of stock\", ex.Message, StringComparison.OrdinalIgnoreCase);\n    }\n\n    \u002F\u002F Common mistake — this does NOT test the exception; it catches and swallows it:\n    \u002F\u002F [Fact]\n    \u002F\u002F public void WrongWay()\n    \u002F\u002F {\n    \u002F\u002F try { service.PlaceOrder(null); }\n    \u002F\u002F catch (ArgumentNullException) { } \u002F\u002F test passes even if no exception thrown!\n    \u002F\u002F }\n}\n```\n\n`Record.Exception` is an alternative that captures the exception without\nasserting type — useful when you want to assert multiple properties of the same\nexception without re-calling the method.\n\n**Rule of thumb:** Always use `Assert.Throws`\u002F`Assert.ThrowsAsync`, never bare\ntry-catch in tests — the bare form cannot distinguish \"threw nothing\" from \"threw\nthe right exception.\"\n",{"id":53,"difficulty":14,"q":54,"a":55},"test-doubles","What are the different types of test doubles and when do you use each?","**Test doubles** are objects that stand in for real dependencies. The five types\ndiffer in how much behavior they provide.\n\n```csharp\n\u002F\u002F 1. Dummy — passed but never used (fills a required parameter)\nvar dummyLogger = new Mock\u003CILogger>().Object;\nvar service = new OrderService(dummyLogger, realRepository);\n\n\u002F\u002F 2. Stub — returns canned answers to method calls\nvar stubInventory = new Mock\u003CIInventoryService>();\nstubInventory.Setup(i => i.IsInStock(\"SKU-1\", 2)).Returns(true);\n\u002F\u002F No verification — we just want a fixed answer\n\n\u002F\u002F 3. Mock — verifies interactions (was a method called?)\nvar mockEmailer = new Mock\u003CIEmailService>();\nservice.PlaceOrder(order);\nmockEmailer.Verify(e => e.SendConfirmation(order.Email), Times.Once);\n\u002F\u002F Fails if SendConfirmation was never called\n\n\u002F\u002F 4. Spy — records calls for later inspection (less common with Moq)\nvar calls = new List\u003Cstring>();\nmockEmailer.Setup(e => e.SendConfirmation(It.IsAny\u003Cstring>()))\n           .Callback\u003Cstring>(email => calls.Add(email));\n\n\u002F\u002F 5. Fake — a working but simplified implementation\n\u002F\u002F An in-memory repository is the classic fake:\npublic class FakeOrderRepository : IOrderRepository\n{\n    private readonly List\u003COrder> _store = new();\n    public void   Add(Order o) => _store.Add(o);\n    public Order? Get(int id)  => _store.FirstOrDefault(o => o.Id == id);\n}\n```\n\nFakes are usually hand-written; stubs, mocks, and spies are usually created\nwith a mocking library like Moq or NSubstitute.\n\n**Rule of thumb:** Use stubs when you need controlled return values, mocks when\nyou need to verify behavior, and fakes when the interaction is too complex to\nstub realistically.\n",{"id":57,"difficulty":14,"q":58,"a":59},"testable-code-design","What design practices make .NET code easier to unit test?","Testability is a design quality, not a testing afterthought. The key practices\nall reduce coupling and surface dependencies explicitly.\n\n```csharp\n\u002F\u002F Hard to test — hidden dependency, static call, can't inject a fake:\npublic class ReportService\n{\n    public string GenerateReport()\n    {\n        var data = Database.Query(\"SELECT ...\"); \u002F\u002F static, untestable\n        return FormatData(data);\n    }\n}\n\n\u002F\u002F Testable — dependency injected, interface-typed, fakeable:\npublic class ReportService\n{\n    private readonly IDataRepository _repo;\n    public ReportService(IDataRepository repo) => _repo = repo;\n\n    public string GenerateReport()\n    {\n        var data = _repo.Query(\"SELECT ...\");\n        return FormatData(data);\n    }\n}\n\n\u002F\u002F Other testability enablers:\n\u002F\u002F 1. Small, focused classes — SRP means less setup per test\n\u002F\u002F 2. Avoid static methods and singletons — they become test pollution\n\u002F\u002F 3. Avoid new-ing dependencies inside methods — use DI instead\n\u002F\u002F 4. Return values rather than mutating parameters — easier to assert\n\u002F\u002F 5. Make side-effecting methods take an interface (IEmailSender, IClock)\n\n\u002F\u002F Testable time — inject IClock so tests can control \"now\":\npublic interface IClock { DateTime UtcNow { get; } }\npublic class SystemClock : IClock { public DateTime UtcNow => DateTime.UtcNow; }\n\n\u002F\u002F In tests:\nvar fakeClock = new Mock\u003CIClock>();\nfakeClock.Setup(c => c.UtcNow).Returns(new DateTime(2026, 1, 1));\n```\n\n**Rule of thumb:** If you cannot instantiate a class in a test without spinning\nup a real database or network, the class is not designed for testability — inject\nits dependencies instead.\n",{"id":61,"difficulty":14,"q":62,"a":63},"code-coverage","What is code coverage and how do you measure it in .NET?","**Code coverage** measures what percentage of production code is executed by\ntests. It is a useful signal but not a goal in itself — 100% coverage does not\nmean the code is correct.\n\n```bash\n# Measure coverage with coverlet (installed via NuGet in test project):\ndotnet add package coverlet.collector\n\n# Run tests and collect coverage:\ndotnet test --collect:\"XPlat Code Coverage\"\n# Produces a coverage.cobertura.xml in TestResults\u002F\n\n# Generate an HTML report with ReportGenerator:\ndotnet tool install -g dotnet-reportgenerator-globaltool\nreportgenerator -reports:\"**\u002Fcoverage.cobertura.xml\" -targetdir:\"coverage-report\"\n```\n\n```csharp\n\u002F\u002F ExcludeFromCodeCoverage — exclude generated or trivial code from metrics:\n[ExcludeFromCodeCoverage]\npublic class AutoGeneratedDto { \u002F* property getters *\u002F }\n\n\u002F\u002F Branch coverage vs line coverage:\npublic string Classify(int n)\n{\n    if (n > 0) return \"positive\"; \u002F\u002F line 1\n    if (n \u003C 0) return \"negative\"; \u002F\u002F line 2\n    return \"zero\";                \u002F\u002F line 3\n    \u002F\u002F 100% line coverage needs 3 tests (one per return)\n    \u002F\u002F 100% branch coverage also needs to test n==0 reaching line 3\n}\n```\n\nAim for high coverage of business logic and edge cases; do not chase coverage\nin generated code, configuration bootstrapping, or trivial property accessors.\n\n**Rule of thumb:** 70–80% line coverage on core business logic is a healthy\ntarget. Coverage below 50% is a warning sign; 100% is usually not worth the\nmaintenance cost unless safety-critical.\n",{"id":65,"difficulty":14,"q":66,"a":67},"xunit-constructor-disposable","How does xUnit use the constructor and IDisposable for test setup and teardown?","xUnit deliberately avoids `[SetUp]`\u002F`[TearDown]` attributes in favour of the\nstandard C# lifecycle: **constructor** for setup and **IDisposable.Dispose**\nfor teardown. A new instance is created per test.\n\n```csharp\npublic class FileProcessorTests : IDisposable\n{\n    private readonly string  _tempFile;\n    private readonly FileProcessor _processor;\n\n    \u002F\u002F Constructor = [SetUp]: runs before EACH test\n    public FileProcessorTests()\n    {\n        _tempFile  = Path.GetTempFileName();\n        _processor = new FileProcessor();\n        File.WriteAllText(_tempFile, \"test data\"); \u002F\u002F prepare test fixture\n    }\n\n    [Fact]\n    public void Process_ValidFile_ReturnsLineCount()\n    {\n        var count = _processor.CountLines(_tempFile);\n        Assert.Equal(1, count);\n    }\n\n    [Fact]\n    public void Process_EmptyFile_ReturnsZero()\n    {\n        File.WriteAllText(_tempFile, \"\");\n        var count = _processor.CountLines(_tempFile);\n        Assert.Equal(0, count);\n    }\n\n    \u002F\u002F IDisposable.Dispose = [TearDown]: runs after EACH test\n    public void Dispose()\n    {\n        if (File.Exists(_tempFile))\n            File.Delete(_tempFile); \u002F\u002F clean up regardless of pass\u002Ffail\n    }\n}\n\n\u002F\u002F For async teardown, implement IAsyncLifetime instead:\npublic class AsyncSetupTests : IAsyncLifetime\n{\n    public async Task InitializeAsync() { \u002F* async setup *\u002F }\n    public async Task DisposeAsync()    { \u002F* async teardown *\u002F }\n    [Fact] public void Test() { }\n}\n```\n\n**Rule of thumb:** Implement `IDisposable` in every test class that acquires\nunmanaged resources (files, sockets, database connections). This ensures cleanup\neven when a test throws.\n",{"id":69,"difficulty":14,"q":70,"a":71},"fluent-assertions","What is FluentAssertions and why do developers prefer it over built-in xUnit asserts?","**FluentAssertions** is a NuGet library that provides a natural-language API\nfor assertions. It produces richer failure messages and reads closer to spoken\nEnglish than the positional `Assert.Equal(expected, actual)` style.\n\n```csharp\n\u002F\u002F Installation:\n\u002F\u002F dotnet add package FluentAssertions\n\n\u002F\u002F xUnit built-in — argument order matters and errors are terse:\nAssert.Equal(expected: 42,      actual: result);   \u002F\u002F \"Expected: 42\\nActual: 43\"\nAssert.True(list.Contains(\"x\")); \u002F\u002F \"Assert.True() Failure: Expected: True, Actual: False\"\n\n\u002F\u002F FluentAssertions — reads as a sentence, errors name the violated condition:\nresult.Should().Be(42);\nlist.Should().Contain(\"x\");\nlist.Should().HaveCount(3).And.NotContain(\"y\");\n\n\u002F\u002F Collection assertions:\norders.Should().AllSatisfy(o => o.Total.Should().BePositive());\norders.Should().BeInAscendingOrder(o => o.PlacedAt);\n\n\u002F\u002F Exception assertions (alternative to Assert.Throws):\nAction act = () => service.PlaceOrder(null);\nact.Should().Throw\u003CArgumentNullException>()\n     .WithParameterName(\"order\")\n     .WithMessage(\"*cannot be null*\");\n\n\u002F\u002F Async:\nFunc\u003CTask> asyncAct = () => service.PlaceOrderAsync(null);\nawait asyncAct.Should().ThrowAsync\u003CArgumentNullException>();\n```\n\nThe failure message for `list.Should().Contain(\"x\")` is:\n`Expected list {\"a\", \"b\"} to contain \"x\".`\nThat's immediately actionable without reading the test body.\n\n**Rule of thumb:** Use FluentAssertions in any project where test failures need\nto be quickly diagnosed by the whole team — the self-describing messages cut\ndebugging time significantly.\n",{"id":73,"difficulty":74,"q":75,"a":76},"inner-classes-helper-methods","hard","How do you organise large test classes to keep them maintainable?","Large test classes become hard to navigate. Three common techniques keep them\nclean: **nested classes**, **shared builder helpers**, and **test base classes**.\n\n```csharp\n\u002F\u002F 1. Nested classes — group tests by method or scenario:\npublic class OrderServiceTests\n{\n    public class PlaceOrder\n    {\n        [Fact] public void WhenNull_Throws() { }\n        [Fact] public void WhenOutOfStock_Throws() { }\n        [Fact] public void WhenValid_ReturnsConfirmed() { }\n    }\n\n    public class CancelOrder\n    {\n        [Fact] public void WhenAlreadyCancelled_Throws() { }\n        [Fact] public void WhenPending_SetsStatusCancelled() { }\n    }\n}\n\u002F\u002F xUnit discovers nested public classes automatically\n\n\u002F\u002F 2. Object Mother \u002F Builder — remove Arrange duplication:\npublic static class OrderBuilder\n{\n    public static Order Valid() => new Order\n        { Sku = \"SKU-1\", Quantity = 1, CustomerId = Guid.NewGuid() };\n\n    public static Order WithQuantity(int qty)\n        => Valid() with { Quantity = qty };\n}\n\n\u002F\u002F In tests:\nvar order = OrderBuilder.WithQuantity(5);\n\n\u002F\u002F 3. Abstract base class — share setup across multiple test classes:\npublic abstract class OrderServiceTestBase\n{\n    protected readonly Mock\u003CIInventoryService> MockInventory = new();\n    protected readonly OrderService Service;\n    protected OrderServiceTestBase()\n        => Service = new OrderService(MockInventory.Object);\n}\n\npublic class PlaceOrderTests : OrderServiceTestBase\n{\n    [Fact] public void Valid_ReturnsConfirmed()\n    {\n        MockInventory.Setup(i => i.IsInStock(It.IsAny\u003Cstring>(), 1)).Returns(true);\n        var result = Service.PlaceOrder(OrderBuilder.Valid());\n        Assert.Equal(OrderStatus.Confirmed, result.Status);\n    }\n}\n```\n\n**Rule of thumb:** Reach for nested classes first — they add structure with\nzero new files. Extract a builder when three or more tests share the same\nArrange block.\n",{"id":78,"difficulty":14,"q":79,"a":80},"testing-async-methods","How do you write unit tests for async methods in xUnit?","xUnit supports `async Task` test methods natively. You `await` the method\nunder test directly in the test body — no `.Result` or `.Wait()`, which\nwould deadlock or swallow exceptions.\n\n```csharp\npublic class OrderServiceAsyncTests\n{\n    [Fact]\n    public async Task PlaceOrderAsync_WhenValid_ReturnsConfirmedOrder()\n    {\n        \u002F\u002F Arrange\n        var mockInventory = new Mock\u003CIInventoryService>();\n        mockInventory\n            .Setup(i => i.IsInStockAsync(\"SKU-1\", 2))\n            .ReturnsAsync(true);\n\n        var service = new OrderService(mockInventory.Object);\n        var order   = new Order { Sku = \"SKU-1\", Quantity = 2 };\n\n        \u002F\u002F Act — await directly; xUnit handles the async state machine\n        var result = await service.PlaceOrderAsync(order);\n\n        \u002F\u002F Assert\n        Assert.Equal(OrderStatus.Confirmed, result.Status);\n    }\n\n    \u002F\u002F Testing async exceptions:\n    [Fact]\n    public async Task PlaceOrderAsync_WhenOutOfStock_ThrowsInvalidOperationException()\n    {\n        var mockInventory = new Mock\u003CIInventoryService>();\n        mockInventory\n            .Setup(i => i.IsInStockAsync(It.IsAny\u003Cstring>(), It.IsAny\u003Cint>()))\n            .ReturnsAsync(false);\n\n        var service = new OrderService(mockInventory.Object);\n\n        \u002F\u002F Assert.ThrowsAsync — await it or the exception goes unobserved:\n        await Assert.ThrowsAsync\u003CInvalidOperationException>(\n            () => service.PlaceOrderAsync(new Order { Sku = \"X\", Quantity = 1 }));\n    }\n\n    \u002F\u002F Bad: .Result deadlocks in some synchronization contexts\n    \u002F\u002F and converts exceptions to AggregateException:\n    \u002F\u002F var result = service.PlaceOrderAsync(order).Result; \u002F\u002F Never do this in tests\n}\n```\n\nMark the test method `async Task` (not `async void`) — `async void` tests\ncomplete before the awaited work finishes, making them silently unreliable.\n\n**Rule of thumb:** Always return `Task` from async tests and `await` every\nasync call. An `async void` test that throws will crash the test runner\nprocess rather than fail the test gracefully.\n",{"id":82,"difficulty":14,"q":83,"a":84},"testing-private-methods","Should you test private methods directly, and if not, how do you ensure they are covered?","Private methods are **implementation details** — they should be tested\nindirectly through the public API. Testing them directly couples tests to\ninternal structure, making refactoring painful without changing behavior.\n\n```csharp\n\u002F\u002F Bad: exposing a private method just to test it directly\npublic class InvoiceService\n{\n    \u002F\u002F Made internal only to allow testing — wrong motivation:\n    internal decimal ApplyLateFee(decimal amount, int daysLate)\n        => amount + (daysLate > 30 ? 50m : 0m);\n}\n\n\u002F\u002F Good: test through the public method that delegates to the private one\npublic class InvoiceServiceTests\n{\n    [Theory]\n    [InlineData(29, 100m, 100m)] \u002F\u002F not late enough — no fee\n    [InlineData(30, 100m, 100m)] \u002F\u002F exactly 30 days — no fee\n    [InlineData(31, 100m, 150m)] \u002F\u002F over 30 days — $50 late fee applied\n    public void FinalizeInvoice_AppliesLateFeeWhenOverdue(\n        int daysLate, decimal subtotal, decimal expected)\n    {\n        var service  = new InvoiceService();\n        var invoice  = new Invoice { Subtotal = subtotal, DaysLate = daysLate };\n\n        var result = service.FinalizeInvoice(invoice); \u002F\u002F public method\n\n        Assert.Equal(expected, result.Total);\n    }\n}\n\n\u002F\u002F When a private method is genuinely complex and hard to reach:\n\u002F\u002F Option 1 — extract it to a separate class with a public method\n\u002F\u002F Option 2 — use [InternalsVisibleTo] + internal access (sparingly)\n\u002F\u002F Option 3 — reflect on it via PrivateObject (legacy MSTest; fragile)\n\n\u002F\u002F The need to test a private method directly is usually a signal that\n\u002F\u002F the method belongs in its own class with a clear public contract.\n```\n\nIf a private method is so complex it needs its own test, extract it into\na collaborating class with a public interface.\n\n**Rule of thumb:** Test behavior, not implementation. If you cannot reach a\nprivate method through the public API with reasonable effort, the class is\nprobably doing too much — split it.\n",15,null,{"description":11},"Unit testing interview questions — xUnit vs NUnit, Arrange-Act-Assert, data-driven tests, test isolation, FluentAssertions, and testable code design.","dotnet\u002Ftesting\u002Funit-testing","Testing","testing","2026-06-23","KYAuIhdndCVv61PUKzOlTq8Jp0dFAZ2UpafMYn-L-yY",[95,96,99],{"subtopic":6,"path":21,"order":20},{"subtopic":97,"path":98,"order":12},"Mocking","\u002Fdotnet\u002Ftesting\u002Fmocking",{"subtopic":100,"path":101,"order":102},"Integration Testing","\u002Fdotnet\u002Ftesting\u002Fintegration-testing",3,{"path":104,"title":105},"\u002Fblog\u002Fdotnet-unit-testing","Unit Testing in .NET with xUnit",1782244119950]