Reputation: 33
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
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
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