Introduction to TestApi – Part 2: Command-Line Parsing APIs
Command-line parsers remind me of linked lists in C++: everybody has written several at various points in their careers. While everybody should write each of those at least once, I doubt that many people out there are particularly excited about writing and re-writing fundamental data structures on a regular basis – it gets old very quickly. Not to mention that doing so is error-prone and decreases the maintainability of a world that’s already hard to maintain.
That’s why modern-day frameworks such as .NET provide standard implementations of the common data structures. And that’s why TestApi provides a reusable command-line parsing APIs via the CommandLineDictionary and CommandLineParser classes, the latter being a type-safe layer on top of the former. Obviously, these are not test APIs per se – they are general utility APIs that happen to be more often used when writing tests.
A few quick examples follow.
Simple Command-Line Parsing
As seen from the first example below, extracting command-line parameters that are primitives is easy. Primitive command-line parameters are either boolean (e.g. the “verbose” flag below), or a key-value pair, that one can extract with the indexer of the CommandLineDictionary instance (see the “testId” key below), just as one would expect from a Dictionary.
//
// EXAMPLE #1: Parsing a command-line such as "RunTests.exe /verbose /testId=123"
//
using System;
using Microsoft.Test;
public class Program
{
public static void Main(string[] args)
{
CommandLineDictionary d = new CommandLineDictionary(args);
bool verbose = d.ContainsKey("verbose");
int testId = Int32.Parse(d["testId"]);
// use the parsed command-line parameters
}
}
By default flags/keys are indicated with the forward slash (“/”) character and values are indicated with the equals character (“=”), but the user can override that upon initialization of of the CommandLineDictionary object:
//
// EXAMPLE #1b: Parsing a command-line such as "RunTests.exe –verbose –testId:123"
//
...
CommandLineDictionary d = new CommandLineDictionary(args, '-', ':');
...
Finally, you one can use the ToString method to get a string representation of the command-line arguments.
Command-Line Argument Structures
Another common pattern when dealing with command-line arguments is populating a structure which contains all parsed arguments. The CommandLineParser class makes this easy:
// EXAMPLE #2:
// Sample for parsing the following command-line:
// Test.exe /verbose /runId=10
// This sample declares a class in which the strongly typed arguments are populated
public class CommandLineArguments
{
bool? Verbose { get; set; }
int? RunId { get; set; }
}
CommandLineArguments a = new CommandLineArguments();
CommandLineParser.ParseArguments(a, args);
Type-Safe Commands
A third common approach is forming strongly-typed commands from the command-line parameters. This is common for cases when the command-line looks as follows:
some-exe COMMAND parameters-to-the-command
The parsing in this case is a little bit more involved:
- Create one class for every supported command, which derives from the Command abstract base class and implements an expected Execute method.
-
Pass an expected command along with the command-line arguments to CommandLineParser.ParseCommand – the method will return a strongly-typed Command instance that can be Execute()-d.
// EXAMPLE #3:
// Sample for parsing the following command-line:
// Test.exe run /runId=10 /verbose
// In this particular case we have an actual command on the command-line (“run”),
// which we want to effectively de-serialize and execute.
public class RunCommand : Command
{
bool? Verbose { get; set; }
int? RunId { get; set; }
public override void Execute()
{
// Implement your "run" execution logic here.
}
}
Command c = new RunCommand();
CommandLineParser.ParseArguments(c, args);
c.Execute();
Besides the parsing logic, CommandLineParser provides a few additional helper methods. One of them is CommandLineParser.PrintCommandUsage, which prints the usage for specific commands (or all supported commands) to the console.
In Conclusion
The command-line parsing APIs released with TestApi provide a simple and “layered” access to the command-line. Strictly speaking these APIs are not test APIs, but have nevertheless been included in TestApi as tests often have a need of parsing parameters on the command-line.