dno11
dno11

Reputation: 33

Using Scenario Outlines with Specflow & Selenium C#

I'm trying to use a scenario outline in Specflow that will generate tests based on the number of examples I have, that I can then write up using selenium via the C# bindings. So I have created following Feature:

Scenario Outline: Login
Given I have navigated to the Login Page
When I enter a valid '<Login>'
And I enter the correct '<Password>'
And I press the Login CTA
Then I am logged into the Home Page

Examples: 
| Login  | Password   |
| LoginA | passwordA  | 
| LoginB | passwordB  |

When I generate the step definitions I get the following:

    [When(@"I enter a valid '(.*)'")]
    public void WhenIEnterAValid(string p0)
    {
        ScenarioContext.Current.Pending();
    }

    [When(@"I enter the correct '(.*)'")]
    public void WhenIEnterTheCorrect(string p0)
    {
        ScenarioContext.Current.Pending();
    }

I have placed these into a SpecFlow Step Definition file

I also get 2 tests generated named

Login("LoginA", "passwordA",null)

Login("LoginB", "passwordB",null)

So far so good (I think). The next steps are to write the code to complete the step definition so that each of the tests will run and use the relevant data. This is the bit I'm stuck on.

I know for example if the data was in a table in a standard Scenario I can call from the table, so a feature like-

Scenario: Login
Given I have navigated to the Login Page
When I enter a valid Login
| Login   |
| loginA  |
And I enter the correct Password
| Password   |
| passwordA |
And I press the Login CTA
Then I am logged into the Home Page

Can be satisfied with code like:

 public void WhenIEnterAValidLogin(Table table)
    {
        IWebElement loginField = WebBrowser.Current.FindElement(By.Name("loginBox"));
        string loginText = table.Rows[0]["Login"].ToString();
        usernameField.SendKeys(loginText);
    }

But basically I have no idea how to write the code so that it looks in the "Examples" table and takes the relevant data for each test. Is this possible or do I have to write the code individually for each test, i.e. a step where I type in "loginA" AND a step where I type in "loginB"? I've looked around the web and haven't found an example to help me.

Thanks for any help in advance! If I'm not making myself clear or am making some basic errors please let me know as I'm new to stack overflow and this is my first post.

Upvotes: 2

Views: 2395

Answers (2)

marwaha.ks
marwaha.ks

Reputation: 549

You could access the stack trace like so. Tried and tested. You're not just limited to one attribute name.

        var stackTraces = new StackTrace();
        foreach (var stackFrame in stackTraces.GetFrames())
        {
            MethodBase methodBase = stackFrame.GetMethod();
            TestPropertyAttribute[] attributes = methodBase.GetCustomAttributes(typeof(TestPropertyAttribute), false) as TestPropertyAttribute[];
            if (attributes != null && attributes.Length >= 1)
            {
                variantName = attributes.FirstOrDefault(x => x.Name.Equals("VariantName"))?.Value;
            }
        }

Upvotes: 0

AutomatedChaos
AutomatedChaos

Reputation: 7500

I think you are thinking too deeply through. If you do the following:

Scenario Outline: Login
Given I have navigated to the Login Page
When I enter a valid '<Login>'
And I enter the correct '<Password>'
And I press the Login CTA
Then I am logged into the Home Page

Examples: 
| Login  | Password   |
| LoginA | passwordA  | 
| LoginB | passwordB  |

you are actually making the following two scenarios:

Scenario: Login_LoginA
Given I have navigated to the Login Page
When I enter a valid 'LoginA'
And I enter the correct 'PasswordA'
And I press the Login CTA
Then I am logged into the Home Page

Scenario: Login_LoginB
Given I have navigated to the Login Page
When I enter a valid 'LoginB'
And I enter the correct 'PasswordB'
And I press the Login CTA
Then I am logged into the Home Page

The steps code gets reused.
Scenario Outlines are a nice way to create a load of scenarios that do the same thing with only different data in a short time.

You are right about the two tests that gets created, this makes it better manageable in your testrunner. The two tests are actual TestMethods that calls an inner method named Login:

   // this method is created to be called from a scenario outline
   // this contains the steps that one iteration of the outline should take.
   public virtual void Login(string login, string password, string[] exampleTags)
    {
        TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Login", exampleTags);
        #line 13
        this.ScenarioSetup(scenarioInfo);
        #line 14
        testRunner.Given("I have navigated to the Login Page", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
        #line 15
        testRunner.When(string.Format("I enter a valid \'{0}\'", login), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
        #line 16
        testRunner.And(string.Format("I enter the correct \'{0}\'", password), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
        #line 17
        testRunner.And("I press the Login CTA", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
        #line 18
        testRunner.Then("I am logged into the Home Page", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
        #line hidden
        this.ScenarioCleanup();
    }

    [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
    [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Login")]
    [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "LoginA")]
    [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:Login", "LoginA")]
    [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:Password", "passwordA")]
    public virtual void Login_LoginA()
    {
        // Calling the inner method Login
        this.Login("LoginA", "passwordA", ((string[])(null)));
    }

    [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
    [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Login")]
    [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "LoginB")]
    [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:Login", "LoginB")]
    [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:Password", "passwordB")]
    public virtual void Login_LoginB()
    {
        this.Login("LoginB", "passwordB", ((string[])(null)));
    }

Upvotes: 2

Related Questions