Complex Types in Step Properties?

Maybe I’m going about this all the wrong way, but what would be the preferred way of defining properties on a test step that are made of more complex types?

The type I would like to use for this property is something like this struct:

public readonly struct MySettingAsStruct
{
    public int Id { get; }
    public string Name { get; }
    public string Checksum{ get; }
    public MySettingAsStruct(int id, string name, string checksum)
    {
        Id = id;
        Name = name;
        Checksum = checksum;
    }
    public override string ToString()
    {
        return $"{Id} - {Name} - {Checksum}";
    }
}

Or possibly this class:

public class MySettingAsClass
{
    public int Id { get; }
    public string Name { get; }
    public string Checksum{ get; }
    public MySettingAsClass(int id, string name, string checksum)
    {
        Id = id;
        Name = name;
        Checksum = checksum;
    }
    public override string ToString()
    {
        return $"{Id} - {Name} - {Checksum}";
    }
}

Then I attempt to use those types as a setting of a TestStep, like so:

[Display("MyTestStep")]
public class MyTestStep : TestStep
{
    [Display("MySettingAsClass")]
    [AvailableValues(nameof(classSettings))]
    public MySettingAsClass? MySettingAsClass { get; set; }

    public MySettingAsClass[] classSettings => new MySettingAsClass[]
    {
        new MySettingAsClass(1,"One","One"),
        new MySettingAsClass(2,"Two", "Two"),
        new MySettingAsClass(3, "Three", "Three")
    };

    [Display("MySettingAsStruct")]
    [AvailableValues(nameof(structSettings))]
    public MySettingAsStruct MySettingAsStruct { get; set; }

    public MySettingAsStruct[] structSettings => new MySettingAsStruct[]
    {
        new MySettingAsStruct(1,"One","One"),
        new MySettingAsStruct(2,"Two", "Two"),
        new MySettingAsStruct(3, "Three", "Three")
    };

    public override void Run()
    {
        Log.Info($"Running {Name}...");
        Log.Info($"Struct ID = {MySettingAsStruct.Id}");
        Log.Info($"Struct Name = {MySettingAsStruct.Name}");
        Log.Info($"Struct Checksum = {MySettingAsStruct.Checksum}");
        Log.Info($"Class ID = {MySettingAsClass?.Id}");
        Log.Info($"Class Name = {MySettingAsClass?.Name}");
        Log.Info($"Class Checksum = {MySettingAsClass?.Checksum}");
    }
}

The behavior that I am seeing is that this works fine when I setup and run a test plan in TUI. I’m able to select from my list of settings, and I see the expected output when I run the test plan.

The issue that I am running into is that the plan cannot be saved. When it gets serialized to XML, this is the output I end up with:

    <TestStep type="TestLibrary1.MyTestStep" Id="fe7adbaf-7abd-4f7b-b189-d6293384e203">
      <MySettingAsClass></MySettingAsClass>
      <MySettingAsStruct>3 - Three - Three</MySettingAsStruct>
      <Name Metadata="Step Name">MyTestStep</Name>
    </TestStep>

The class version has no value and throws an error on load, while the struct stores the ToString() representation that does not get deserialized properly and results in the test step loading with defaulted values.

I tried using the [EmbedProperties] attribute, but that doesn’t seem to quite do what I want. Applying that attribute causes the property not to appear in the settings panel at all, unless I provide setters for all of the properties. However, that allows the user to modify each individually, which is not the behavior I want.

Am I missing something simple that would allow this to work properly?

Hi @Jim_B,

What is missing here is a way for the serializer to construct an object from generated XML.

The easiest way is to create use a class, add a parameterless constructor and add a setter to your properties.

If you want to work with objects as structs or class without a parameterless constructor, there is no standard way to build an object from the XML code. Then you’d need to create a serializer plugin. The SDK examples, has some sample code on this.

Hi @rolf_madsen,

Thanks! I tried adding public setters to the Struct version at one point, but I guess the missing piece of the puzzle for me was that I didn’t realize the serializer apparently can’t handle structs.

Adding them to the class version was apparently not one of the permutations I tried, because that does indeed work. Adding my own serializer also seems to be working.

Thanks again!

2 Likes