nc_program_manager/.paul/codebase/TESTING.md
dtrentin dabcf989d9 docs: map existing codebase
- STACK.md - Technologies and dependencies (.NET 4.7.2, NuGet, Costura.Fody)
- ARCHITECTURE.md - Layered CLI with ICncMachine factory pattern
- STRUCTURE.md - Directory layout and where to add new code
- CONVENTIONS.md - C# style, naming, result wrapper pattern
- TESTING.md - NUnit 3 + Moq, stubs, ignored FOCAS tests
- INTEGRATIONS.md - FOCAS, LSV2, FTP, RSA licensing
- CONCERNS.md - Blocking async, silent catch, no logging, Windows-only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:48:20 +02:00

4.8 KiB

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:

# 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.csNcProgramValidatorTests.cs, FanucMachineTests.cs
  • Integration: {Subject}IntegrationTests.csHeidenhainMachineIntegrationTests.cs
  • Stubs: {Protocol}ServerStub.csFtpServerStub.cs, Lsv2ServerStub.cs

Test Structure

Fixture Pattern:

[TestFixture]
public class NcProgramValidatorTests
{
    [SetUp]
    public void SetUp() { ... }

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

Parameterized Tests:

[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:

[Category("Integration")]  // marks integration tests

Mocking

Interface mocking via Moq:

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):

// 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:

[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
  • IsoProgramFormatterIsoProgramFormatterTests.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