product: maestro audience: test-developer authority: normative
Local Debugging with MSTest
Running test code locally with MSTest is the standard workflow — write and debug C# measurement functions before deploying to the real system.
Disable parallelization (MANDATORY)
The MSTest project template enables method-level parallelization by default. For
hardware tests this IS dangerous — multiple tests execute simultaneously, causing
concurrent PowerOn, Connect, and PowerOff calls against the same board.
Add this to a dedicated MSTestSettings.cs file in the test project:
[assembly: DoNotParallelize]
MUST NOT be inlined in the test class. MUST be in a separate MSTestSettings.cs file.
Without this, you will see intermittent hardware failures caused by concurrent test methods accessing the same instruments simultaneously.
Mapping YAML concepts to MSTest
| YAML concept | MSTest equivalent | Notes |
|---|---|---|
setup: steps |
[ClassInitialize] |
Runs once before all tests in the class |
teardown: + run_on_abort: true |
[ClassCleanup] |
Runs once after all tests, even on failure |
Each steps: entry |
[TestMethod] |
One test method per measurement step |
measurement: limits |
Assert calls |
Use the same limit values as YAML |
variables: block |
const or static fields |
Shared constants across test methods |
MUST NOT use [TestInitialize] / [TestCleanup] for hardware setup/teardown — these
run around each individual test method, causing repeated power cycles between every
step.
Supplying system-injected parameters locally
In a live run, values like serial number and station config are injected by the system. Declare them as constants at the top of the test class with comments showing their system source:
[TestClass]
public sealed class FunctionalTestsTests
{
// -------------------------------------------------------------------------
// Local settings — values the system injects at runtime.
// Change these to match your local development setup.
// -------------------------------------------------------------------------
private const string AgentHost = "http://agent64.local:5000"; // cfg.AGENT_HOST
private const string ProductId = "ESH10000121"; // exec.tag.ProductId
private const string ProductRevision = "1"; // exec.tag.ProductRevision
private const string SerialNumber = "DEV-001"; // exec.serial_number
[ClassInitialize]
public static void Setup(TestContext _)
{
FunctionalTests.Connect(AgentHost);
FunctionalTests.LoadModule();
}
[ClassCleanup]
public static void Teardown()
{
FunctionalTests.Disconnect();
}
[TestMethod]
public void TestProgramDevice()
{
FunctionalTests.ProgramDevice(ProductId, ProductRevision, SerialNumber);
}
}
Use a recognisable dev serial number (e.g. DEV-001) so that results accidentally
recorded against a real system are easy to identify and discard.
MUST NOT scatter magic strings through individual test methods.
Full example
using Microsoft.VisualStudio.TestTools.UnitTesting;
[assembly: DoNotParallelize]
namespace MyTests;
[TestClass]
public class FunctionalTests
{
private const string AgentHost = "http://agent64.local:5000"; // cfg.AGENT_HOST
private const string Rail = "3V3";
private const double LowLimit = 3.135;
private const double HighLimit = 3.465;
private static HardwareDriver _hw = null!;
[ClassInitialize]
public static void Setup(TestContext _)
{
_hw = new HardwareDriver(AgentHost);
_hw.PowerOn();
_hw.Connect();
}
[ClassCleanup]
public static void Teardown()
{
_hw?.PowerOff();
_hw?.Dispose();
}
[TestMethod]
public void Measure_3V3_Rail()
{
double voltage = _hw.ReadRail(Rail);
Assert.IsTrue(voltage >= LowLimit && voltage <= HighLimit,
$"RAIL_3V3: {voltage} V outside [{LowLimit}, {HighLimit}]");
}
}