How to test parseArgument
So given
var myOptions = new Option<string[]>(
name: "--my",
description: "items",
parseArgument: ParseMyItems);
and
internal static string[] ParseMyItems(ArgumentResult result)
{
//manipulate/validate/parse items
return result.Values();
}
I want to test ParseMyItems. so i need to be able to create an instance of ArgumentResult. but the constructor is internal
internal ArgumentResult(
Argument argument,
SymbolResult? parent) : base(argument, parent)
{
Argument = argument;
}
So how do i get an instance of ArgumentResult.
Currently i have this hack:
public static class ArgumentBuilder
{
static Func<string[], ArgumentResult> construct;
static ArgumentBuilder()
{
var tokensField = typeof(ArgumentResult).GetField("_tokens", BindingFlags.NonPublic | BindingFlags.Instance)!;
var constructor = typeof(ArgumentResult).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)
.Single();
construct = inputs =>
{
var invoke = constructor.Invoke(
new object?[]
{
new Argument<string[]>(_ => inputs),
null
})!;
var result = (ArgumentResult) invoke;
var tokens = (List<Token>) tokensField.GetValue(result)!;
tokens.AddRange(inputs.Select(_ => new Token(_, TokenType.Argument, null!)));
return result;
};
}
public static ArgumentResult Build(params string[] values) =>
construct(values);
}
then i can do
var argument = ArgumentBuilder.Build("value1", "value2");
ParseMyItems(argument)
but there must be a better way
it would be great if this docs could have code that show how to test AddValidator and parseArgument https://learn.microsoft.com/en-us/dotnet/standard/commandline/model-binding#custom-validation-and-binding
You could perhaps wrap the method under test (like ParseMyItems) in its own test method that will assert the MUT's behavior. Basically, (pseudo code!):
void Test()
{
var myOptions = new Option<string[]>(
name: "--my",
description: "items",
parseArgument: Test_ParseMyItems
);
var parseResult = myOptions.Parse(new string[] {"--my", "foo", "bar"});
// Assert parseResult state of interest
static string[] Test_ParseMyItems(ArgumentResult argResult)
{
var optionParseResult = ParseMyItems(argResult);
// Assert state of optionParseResult
return optionParseResult;
}
}
That way, the ArgumentResult is created the way it is supposed to be without you needing to try mimicking the behavior of parts of the library itself.
That way, the ArgumentResult is created the way it is supposed to be without you needing to try mimicking the behavior of parts of the library itself.
I second this approach. Correctly recreating some of these result types in the absence of an actual parse would be fragile. (For example, your reflection code won't work against the code in main today.) We don't test ArgumentResult in isolation in this way even within System.CommandLine's tests. We test exclusively through the public API. So to get the ArgumentResult to pass to the method you want to test, I would recommend parsing some input and getting the ArgumentResult something like this: command.Parse("one two three").GetResult(argument).