0

This is for a test automation using c# and Specflow (cucumber for c#)

I have a class with urls like this:

class Paths  
{
  public static string google_home   = "www.google.com";
  public static string another_url   = "www.something.com";
  public static string facebook_home = "www.facebook.com";
}

And I have a step definition like this:

[Then(@"I navigate to ""(.*)""")]  

public void INavigateTo (string url)
    {   
        Configuration.driver.Navigate().GoToUrl(Paths.url);
    }

Of course this scrip doesnt work, but I just want to explain the idea. By using the step definition like this:

I navigate to "google_home"

I obtain the string "google_home", and I want to use this string to directly choose the option Paths.google_home. I dont want to use a switch to do the assignation, since there are hundreds of urls.

EDIT: The linked solution would be something like this:

var values = typeof(Paths).GetFields();
Configuration.driver.Navigate().GoToUrl(values[0].GetValue(null).ToString());

I have no idea of knowing what number of the array "values" corresponds with the string I am looking for. As such I dont consider it a solution.

pfernandez
  • 303
  • 2
  • 12
  • Sounds like a case for using Reflection. – David Arno May 15 '15 at 10:48
  • I think this question could have a Specflow specific answer, which, although using a switch, would isolate in a single place using a step argument transaformation. please reask without the focus on reflection, or maybe the question could be reopened @servy? – Sam Holder May 19 '15 at 14:30
  • @SamHolder The question specifically states that that isn't an option: "[...] dont want to use a switch to do the assignation, since there are hundreds of urls" – Servy May 19 '15 at 14:33
  • @servy I know, but the question has nothing to do with Specflow per se. but as the OP is using specflow I think an answer which points out a better way of using the Specflow infrastructure is possible, which is agnostic of using reflection or a switch statement for the final method of getting the path. If the question was only 'how do I get a property value via reflection' then I can see its definitely a duplicate, but it has slightly more context than that, and an answer which points out how to do the conversion in a specflow-y way and links to the duplicate answer is possible. just my $0.02 – Sam Holder May 19 '15 at 14:45
  • anyway I'll add a comment and leave it at that – Sam Holder May 19 '15 at 14:45
  • since you are using Specflow, I'd use a `[StepArgumentTransformation]` method to transform to a `Uri` instance based on the given string. Then in the transformation method you can either use reflection to get the url in the property as with the linked answer, or implement your switch in a single place. – Sam Holder May 19 '15 at 14:49
  • Interesting Sam, I am currently investigating about 'StepArgumentTransformation'. I wonder if it is possible to pass other things, like a IWebElement. Maybe the URLs example is a bad one. – pfernandez May 19 '15 at 15:15
  • it only allows you to convert from the string argument in the step definition to a different type, but if you rewrote your `Then` step to take a `Uri` as the argument rather than a string, then specflow will look for a method which is attributed with [StepArgumentTransformation] which takes a string and returns a `Uri` and will call that. If you had a custom type, like say `MyUri` then you could have a transform to this type, which did the reflective lookup. If you then made this type implicitly convertible to a string then you could just pass that type into the `GoToUrl` method. – Sam Holder May 19 '15 at 15:23

1 Answers1

0

Its not too difficult to do:

public static class Paths
{
    public static string Path1 = "";
    public static string Path2 = "";
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class NavigationTargetAttribute : Attribute
{
    public string Target { get; set; }

    public NavigationTargetAttribute(string target)
    {
        Target = target;
    }
}

public class MyNavigationClass
{
    [NavigationTarget(Paths.Path1)]
    public void MyNavigateMethod()
    {
        string target = GetNavigationTarget();
    }

    private string GetNavigationTarget([CallerMemberName]string caller = null)
    {
        object[] attribs = this.GetType()
                               .GetMethod(caller)
                               .GetCustomAttributes(
                               typeof(NavigationTargetAttribute), false);

        if (attribs == null)
            return "some default target";

        return ((NavigationTargetAttribute)attrib[0]).Target;
    }
}

I wouldn't name it Then, but this is what I would do. The custom attribute is set to decorate methods, and not allow multiple copies of the same attribute.

The GetNavigationTarget uses a C#4 feature called CallerMemberName which returns the name of the method calling the function, which is located in the System.Runtime.CompilerServices namespace.

There are some things you want to check, for example a valid navigation target. I'm not sure what the string in your example does but you can add transformation functions in the attribute if you want.

Ron Beyer
  • 11,003
  • 1
  • 19
  • 37
  • This looks like killing a sparrow with a howitzer, in Ruby I would just do something like: Configuration.driver.NavigateToUrl(Paths.eval(url)) Thanks anyway – pfernandez May 15 '15 at 14:01
  • But once you build the howitzer, its so much easier to kill the birds! Really nothing equivalent like Ruby for C#, since C# isn't a dynamic language like Ruby or Javascript. – Ron Beyer May 15 '15 at 18:26