REST-API vs Runner
The OpenTAP Runner represents an evolution from the previous OpenTAP REST-API. Our experience highlighted challenges in supporting cloud scenarios with the OpenTAP REST-API, particularly when attempting to access a PC behind a firewall or within a subnet.
To address these challenges, we integrated NATS technology, retaining the core business logic of the OpenTAP REST-API, but replacing ASP.NET Core with NATS.
Given the nature of the changes involved in this transition, particularly the shift in the underlying transport mechanism, some breaking changes were unavoidable. To signal this significant change and the departure from the REST-API model, we established the OpenTAP Runner.
In the past, we provided an autogenerated C# client for the OpenTAP REST-API (OpenTAP.RestApi.Clients.CSharp). However, in light of our experiences, we elected to manually create a C# client (OpenTAP.Runner.Client) and a TypeScript client (OpenTap Runner Client) for the OpenTAP Runner. These have been designed with improvements derived from our experiences with the REST-API client.
Compatibility
Runner Client 3.x aligns with Runner 2.x and replaces the earlier synchronous 2.x client. For Runner 1.x, keep using Runner Client 2.x.
Migrating from REST-API to Runner
Migrating from OpenTAP REST-API to OpenTAP Runner is trivial if the provided C# or TypeScript clients were consumed. If so, the migration consists of two steps:
- Exchange the client package
C# Client: Remove the NuGet REST-API client dependency OpenTAP.RestApi.Clients.CSharp and instead add the Runner client dependency OpenTAP.Runner.Client
TypeScript Client: Remove the NPM REST-API client dependency opentap-restapi-clients and instead add the Runner client dependency @opentap/runner-client
- Update calling code to use the asynchronous APIs exposed by Runner Client 3.x.
The API surface is largely the same, but methods now return Task
and have Async
suffixes to better align with asynchronous transports.
The following C# example converts a code snippet which retrieves test step types, adds a delay step to the testplan, modifies the Time Delay
property on the delay step to 3s
, runs the test plan and waits for the test plan execution to complete.
The REST-API code would look like this:
var types = await sessionClient.GetTestStepTypesAsync();
var plan = await sessionClient.GetTestPlanAsync(null);
plan.ChildTestSteps.Clear();
plan.ChildTestSteps.Add(types.FirstOrDefault(s => s.TypeName.Contains("Delay")));
plan = await sessionClient.SetTestPlanAsync(plan);
var delayStepSettings = await sessionClient.GetSettingsAsync(plan.ChildTestSteps[0].Id);
if (delayStepSettings.FirstOrDefault(s => s.PropertyName == "DelaySecs") is TextBoxControl textBox)
textBox.StringValue = "3 s";
delayStepSettings = await sessionClient.SetSettingsAsync(plan.ChildTestSteps[0].Id, delayStepSettings);
var status = await sessionClient.RunTestPlanAsync();
while (status.ExecutionState != ExecutionState.Idle)
{
await Task.Delay(1000);
status = await sessionClient.GetStatusAsync(0);
}
The migrated Runner code:
var types = await sessionClient.GetStepTypesAsync();
var plan = await sessionClient.GetTestPlanAsync(null);
plan.ChildTestSteps.Clear();
plan.ChildTestSteps.Add(types.FirstOrDefault(s => s.TypeName.Contains("Delay")));
plan = await sessionClient.SetTestPlanAsync(plan);
var delayStepSettings = await sessionClient.GetSettingsAsync(plan.ChildTestSteps[0].Id);
if (delayStepSettings.FirstOrDefault(s => s.PropertyName == "DelaySecs") is TextBoxControl textBox)
textBox.StringValue = "3 s";
delayStepSettings = await sessionClient.SetSettingsAsync(plan.ChildTestSteps[0].Id, delayStepSettings);
var status = await sessionClient.RunTestPlanAsync(new List<Parameter>());
while (status.SessionState != SessionState.Idle)
{
await Task.Delay(1000);
status = await sessionClient.GetStatusAsync();
}
Although the majority of the code migration is trivial, the RunnerClient and SessionClient creation is different due to the underlying transport mechanism change from REST to NATS.
The following snippets demonstrates the migration of REST client construction to Runner client construction:
HttpClient httpClient = new HttpClient(handler);
SessionManagerClient sessionManagerClient = new SessionManagerClient(httpClient) { BaseUrl = "https://localhost:20116" };
var session = await sessionManagerClient.StartSessionAsync();
SessionClient sessionClient1 = new SessionClient(httpClient) { BaseUrl = session.Url };
The migrated code:
using NATS.Client.Core;
using OpenTap.Runner.Client;
await using INatsConnection connection = new NatsConnection(new NatsOpts { Url = RunnerClient.DefaultUrl });
RunnerClient runner = new RunnerClient(connection);
SessionClient sessionClient = await runner.StartSessionAsync();