1

I'm working with specflow and selenium, and I'm making a table to structure the user inputs in a website.

| Input1 | Input2 | Input3 |
| Opt1   | OptX   | Opt2   |

And I built a class for the input:

public class ObjectDTO 
{
    public string Input1;
    public Input2Type Input2;
    public string Input3;
}

And an enum for one specific opt (since its a static input)

public enum Input2Type
{
    [StringValue("OptX")]
    X,
    [StringValue("OptY")]
    Y,
    [StringValue("OptZ")]
    Z
}

When I get the input I try to instantiate the object:

ObjectDTO stocks = table.CreateInstance<ObjectDTO>();

But it says 'No enum with value OptX found'.

Hélder Gonçalves
  • 3,822
  • 13
  • 38
  • 63
  • 4
    `OptX` is the value for the custom attribute you have mentioned you would need a mechanism to match that value and get the corresponding Enum, this might help http://stackoverflow.com/questions/10426444/enum-set-to-string-and-get-sting-value-when-need – V4Vendetta Dec 11 '12 at 11:19
  • is a class from specflow - TechTalk.SpecFlow.Table – Hélder Gonçalves Dec 11 '12 at 11:26

1 Answers1

1

Unfortunately the table.CreateInstance() is a fairly naive method and fails for Enums (among other things). I ended up writing an extension method for table that uses reflection to interrogate the object instance it is trying to create. The nice thing about this approach is that the method will only overwrite the instance values that are specified in the table columns, however, you have to call it for each row of the table and pass in an already instantiated instance. It could be easily modified to act just like the CreateInstance method, but for my purposes, this worked better...

public static class TableExtensions
{
  public static void FillInstance(this Table table, TableRow tableRow, object instance) {
    var propertyInfos = instance.GetType().GetProperties();
    table.Header.Each(header => Assert.IsTrue(propertyInfos.Any(pi => pi.Name == header), "Expected to find the property [{0}] on the object of type [{1}]".FormatWith(header, instance.GetType().Name)));
    var headerProperties = table.Header.Select(header => propertyInfos.Single(p => p.Name == header)).ToList();
    foreach (var propertyInfo in headerProperties) {
      object propertyValue = tableRow[propertyInfo.Name];
      var propertyType = propertyInfo.PropertyType;
      if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) {
        propertyType = propertyType.GetGenericArguments().Single();
      }

      var parse = propertyType.GetMethod("Parse", new[] { typeof(string) });
      if (parse != null) {
        // ReSharper disable RedundantExplicitArrayCreation
        try {
          propertyValue = propertyType.Name.Equals("DateTime") ? GeneralTransformations.TranslateDateTimeFrom(propertyValue.ToString()) : parse.Invoke(null, new object[] { propertyValue });
        } catch (Exception ex) {
          var message = "{0}\r\nCould not parse value: {2}.{3}(\"{1}\")".FormatWith(ex.Message, propertyValue, parse.ReturnType.FullName, parse.Name);
          throw new Exception(message, ex);
        }
        // ReSharper restore RedundantExplicitArrayCreation
      }

      propertyInfo.SetValue(instance, propertyValue, null);
    }
  }
}

And you could invoke it as follows:

ObjectDTO objectDTO;
foreach (var tableRow in table.Rows)
{
  objectDTO = table.FillInstance(tableRow, new ObjectDTO());
  //Do individual row processing/testing here...
}
Randall Borck
  • 804
  • 9
  • 10