# Testing Patterns

**Analysis Date:** 2026-05-18

## Test Framework

**Runner:**
- NUnit 3.13.3 — `NcProgramManager.Tests/packages.config`
- NUnit3TestAdapter 4.2.1 — VS/dotnet test runner integration
- Config: `NcProgramManager.Tests/nunit.runsettings`

**Assertion Library:**
- NUnit fluent API: `Assert.That(actual, Is.True)`, `Is.EqualTo()`, `Is.InstanceOf<T>()`

**Mocking:**
- Moq 4.18.4 for interface mocking
- Castle.Core 5.1.1 (Moq dependency)
- Custom delegates for `ref` parameter workaround (Moq limitation with P/Invoke refs)
  - Example: `NcProgramManager.Tests/Unit/FanucMachineTests.cs`

**Run Commands:**
```bash
# Standard NUnit runner or VS Test Explorer
dotnet test NcProgramManager.Tests/NcProgramManager.Tests.csproj
# Or via Dockerfile:
docker build -f Dockerfile.test .
```

## Test File Organization

**Location:**
- All tests in `NcProgramManager.Tests/` (separate project)
- Unit tests: `NcProgramManager.Tests/Unit/`
- Integration tests: `NcProgramManager.Tests/Integration/`
- Stubs: `NcProgramManager.Tests/Integration/Stubs/`

**Naming:**
- Unit: `{Subject}Tests.cs` — `NcProgramValidatorTests.cs`, `FanucMachineTests.cs`
- Integration: `{Subject}IntegrationTests.cs` — `HeidenhainMachineIntegrationTests.cs`
- Stubs: `{Protocol}ServerStub.cs` — `FtpServerStub.cs`, `Lsv2ServerStub.cs`

## Test Structure

**Fixture Pattern:**
```csharp
[TestFixture]
public class NcProgramValidatorTests
{
    [SetUp]
    public void SetUp() { ... }

    [Test]
    public void MethodName_Condition_ExpectedResult()
    {
        // arrange
        // act
        // assert (NUnit fluent)
    }
}
```

**Parameterized Tests:**
```csharp
[TestCase(CncManufacturer.Fanuc)]
[TestCase(CncManufacturer.Siemens)]
[TestCase(CncManufacturer.Heidenhain)]
[TestCase(CncManufacturer.Mitsubishi)]
public void Validate_TabCharacter_ReturnsWarning(CncManufacturer m) { ... }
```
Example: `NcProgramManager.Tests/Unit/NcProgramValidatorTests.cs`

**Async Tests:**
- No `async Task` test methods — use `.GetAwaiter().GetResult()` to block
  - Example: `NcProgramManager.Tests/Integration/HeidenhainMachineIntegrationTests.cs`

**Test Categories:**
```csharp
[Category("Integration")]  // marks integration tests
```

## Mocking

**Interface mocking via Moq:**
```csharp
var mockDialect = new Mock<IFocasDialect>();
mockDialect.Setup(d => d.Connect(...)).Returns(...);
var machine = new FanucMachine(config, mockDialect.Object);
```
Example: `NcProgramManager.Tests/Unit/FanucMachineTests.cs`

**Reflection-based state injection (for bypassing protected fields):**
```csharp
// Set internal connection state without real hardware
typeof(FanucMachine)
    .GetField("_state", BindingFlags.NonPublic | BindingFlags.Instance)
    .SetValue(machine, ConnectionState.Connected);
```
Example: `NcProgramManager.Tests/Unit/FanucMachineTests.cs`

**What to mock:**
- Protocol interfaces: `IFocasDialect`, `ILsv2Client`, `ISiemensFtpClient`, `IMitsubishiFtpClient`
- Licensing: `IMachineFingerprint`, `ILicenseValidator`

## In-Process Protocol Stubs

**FtpServerStub** (`NcProgramManager.Tests/Integration/Stubs/FtpServerStub.cs`):
- Minimal FTP server for Siemens/Mitsubishi integration testing
- In-memory file store: `Dictionary<string, string> Files`
- Supported commands: USER, PASS, CWD, TYPE, PASV, LIST, NLST, RETR, STOR, DELE
- Usage: Start before test, inject port into machine config

**Lsv2ServerStub** (`NcProgramManager.Tests/Integration/Stubs/Lsv2ServerStub.cs`):
- Minimal LSV2 TCP server for Heidenhain integration testing
- Implements LSV2 framing (STX, SIZE, CMD, data, CRC)
- Thread-per-client architecture

## Ignored Tests

**FOCAS DLL-dependent tests marked `[Ignore]`:**
- All tests requiring actual `Fwlib32.dll` calls are ignored
- Reason: Cannot mock static P/Invoke calls; DLL requires real Fanuc hardware on Windows
- Example: `NcProgramManager.Tests/Unit/FanucMachineTests.cs`

**Pattern for skipping:**
```csharp
[Test]
[Ignore("Requires Fanuc FOCAS DLL and real hardware")]
public void ConnectAsync_ValidConfig_Connects() { ... }
```

## Coverage Approach

**Well-covered:**
- `NcProgramValidator` — universal rules + all 4 manufacturers, 30+ test cases
  - `NcProgramManager.Tests/Unit/NcProgramValidatorTests.cs`
- `FanucProgram` parsing — `FanucProgramParsingTests.cs`
- `IsoProgramFormatter` — `IsoProgramFormatterTests.cs`
- `CncMachineFactory` dispatch — `CncMachineFactoryTests.cs`
- `LicenseValidator` — 6 unit tests via internal ctor injection — `LicenseValidatorTests.cs`

**Partially covered (blocked by hardware):**
- `FanucMachine` connect/read/write — FOCAS tests `[Ignore]`d
- Heidenhain/Siemens/Mitsubishi machines — integration tests use stubs

**Not covered:**
- `MachineFingerprint` (WMI, Windows-only, no mocked tests)
- `LicenseFile` parsing edge cases
- `Program.cs` CLI orchestration (no CLI-level tests)

---

*Testing analysis: 2026-05-18*
*Update when test framework or patterns change*
