如何在场景大纲中的每个示例 运行 之后存储 ID?
How to store an ID after each Example is run in Scenario Outline?
您好,我是 Specflow 和编码的新手。
我试图在一些全局声明的参数(变量)中存储一个值。但是,一旦执行了第二个示例(在场景大纲中),参数 returns Null。我的所有参数都返回 null。
Examples:
| UserID | Name|
| 111001 | AS |
| 114028 | AK | //2nd Example
在每个场景大纲之后 运行 我需要存储一个 ID。我的功能文件中总共有 4 个示例:场景大纲。
我的专题文件
Scenario Outline: 8. GetUserNumber
Given I enter <UserID>, <Name>,
And I click on Create button
And I verify if<UserID>, <Name> is same as selected in previous page
And I store the UserNumber
And I click on back button
Examples:
| UserID | Name|
| 111001 | AS |
| 114028 | AK |
| 131001 | ASF|
| 134028 | AKA|
我的代码
public class User
{
string PUP1; //Global declared variable
string PUP2; //Global declared variable
public void GetUserNum()
{
string UserNumPath;
if (UserGrade== "G" || Age== "7")
{
UserNumPath= driver.FindElement(By.Id("UserId")).Text;
PUP1 = (UserNumPath);// assigning UserNumPath to PUP1
}
else if (UserGrade== "H" || Age== "4")
{
UserNumPath= driver.FindElement(By.Id("UserId")).Text;
PUP2= (UserNumPath); // assigning UserNumPath to PUP2
}
}
}
如何在每个场景大纲示例执行后保存 ID?
为了帮助说明我的答案,这里有一个非常简单的 class 这是我测试的主题:
public class Animal
{
public Animal(string name)
{
this.Name = name;
}
public string Name { get; }
public string Speak()
{
switch (this.Name)
{
case "cat":
return "meow";
case "dog":
return "woof";
case "cow":
return "moo";
default:
return "I don't know";
}
}
}
这是我的功能文件:
Feature: Animal
Scenario: Animals say the right thing
Given I am a "<AnimalName>"
When I speak
Then I say "<Says>"
Examples:
| AnimalName | Says |
| dog | woof |
| cat | meow |
| cow | moo |
以及该功能文件中测试步骤的实现:
using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Infrastructure;
using Xunit;
[Binding]
public class AnimalSteps
{
private readonly ISpecFlowOutputHelper outputHelper;
private Animal animal;
private string whatISaid;
private static int someStaticField;
public AnimalSteps(ISpecFlowOutputHelper outputHelper)
{
this.outputHelper = outputHelper;
someStaticField++;
this.outputHelper.WriteLine($"In constructor: someStaticField is now {someStaticField}");
SomeStaticClass.Counter++;
this.outputHelper.WriteLine($"In constructor: SomeStaticClass.Counter is now {SomeStaticClass.Counter}");
}
[Given(@"I am a ""(.*)""")]
public void Given_IAmA(string animalName)
{
this.animal = new Animal(animalName);
}
[When(@"I speak")]
public void When_ISpeak()
{
this.outputHelper.WriteLine($"Storing what the animal said. Previous value: '{this.whatISaid}'");
this.whatISaid = this.animal.Speak();
}
[Then(@"I say ""(.*)""")]
public void Then_ISay(string whatToSay)
{
Assert.Equal(whatToSay, this.whatISaid);
}
}
这个 class 值得一些解释。
首先,有一个名为 outputHelper
的私有 ISpecFlowOutputHelper
字段。不用太担心这个,它只是一种将我自己的消息写到测试 运行ner 显示测试结果的地方的一种方式(我正在使用 Visual Studio 2019 的测试浏览器,它写这些,连同 SpecFlow 的一些输出,到每个测试用例的测试详细信息摘要)。
然后还有两个私有实例字段,分别叫做animal
和whatISaid
。这些用于在 AnimalSteps
class 中的不同方法之间共享信息,并且因为它们是实例字段,所以它们特定于 AnimalSteps
class 的当前实例,即每次创建一个新的 AnimalSteps
对象时,它都有自己的这些字段值,完全独立于任何其他 AnimalSteps
对象中那些字段的值。因为它们是私有的,所以它们只能从 AnimalSteps
class 内部访问,所以它们不能真正称为全局。
还有一个名为 someStaticField
的私有静态字段。这也只能从 AnimalSteps
class 中访问,但因为它是静态的(与实例相反),所以它的值在 AnimalSteps
class 的所有实例之间共享。这也是为什么当引用其他字段时引用此字段没有前缀 this.
- 此字段不是 AnimalSteps
的 this
实例的成员,它是成员AnimalSteps
.
的所有实例
所以让我们看一下输出(这是它在 VS 测试资源管理器中的样子,根据您使用的测试 运行 不同,它看起来可能会有所不同):
Given I am a "cat"
-> In constructor: someStaticField is now 1
-> In constructor: SomeStaticClass.Counter is now 1
-> done: AnimalSteps.Given_IAmA("cat") (0.0s)
When I speak
-> Storing what the animal said. Previous value: ''
-> done: AnimalSteps.When_ISpeak() (0.0s)
Then I say "meow"
-> done: AnimalSteps.Then_ISay("meow") (0.0s)
Given I am a "cow"
-> In constructor: someStaticField is now 3
-> In constructor: SomeStaticClass.Counter is now 3
-> done: AnimalSteps.Given_IAmA("cow") (0.0s)
When I speak
-> Storing what the animal said. Previous value: ''
-> done: AnimalSteps.When_ISpeak() (0.0s)
Then I say "moo"
-> done: AnimalSteps.Then_ISay("moo") (0.0s)
Given I am a "dog"
-> In constructor: someStaticField is now 2
-> In constructor: SomeStaticClass.Counter is now 2
-> done: AnimalSteps.Given_IAmA("dog") (0.0s)
When I speak
-> Storing what the animal said. Previous value: ''
-> done: AnimalSteps.When_ISpeak() (0.0s)
Then I say "woof"
-> done: AnimalSteps.Then_ISay("woof") (0.0s)
这里有几点需要注意:
whatISaid
字段的值在每个测试用例中都是一个空字符串,直到它在该测试用例中被设置。这是因为它是一个实例字段,每个测试用例都使用 AnimalSteps
. 的新实例
someStaticField
字段的值每次都不同。这是因为它在 AnimalSteps
的所有实例之间共享,并且 AnimalSteps
构造函数在每次创建新实例时将其加 1。
- 我们可以从
someStaticField
的值中看出,测试用例的顺序是 运行 猫、狗、牛。这与它们在特征文件中出现的顺序(狗、猫、牛)以及它们在 VS 测试资源管理器中出现的顺序(猫、牛、狗)都不相同。
最后一点很重要,因为 SpecFlow 所做的是为您的代码生成单元测试,单元测试应该相互独立;您不能对它们 运行 的顺序做出任何假设,并且一个测试不应依赖于另一个已经 运行.
的测试
我还没有提到 SomeStaticClass
。这是它的样子:
public static class SomeStaticClass
{
public static int Counter { get; set; }
}
这有点像静态 someStaticField
字段,只是它不是 AnimalSteps
class 私有的,您可以从应用程序的任何位置访问它及其成员,无需创建它的实例,然后不必跟踪该实例是什么。如果你真的想要全局声明的变量,那么静态 classes 可能是最好的方法。
我希望您的问题离题有助于理解范围以及在您的应用程序及其测试中可以从何处访问的内容。但要点是,尝试将值从一个单元测试传递到另一个单元测试并不是一个好主意,因为您无法确定它们 运行 的顺序。如果您的测试用例需要检查特定操作的结果,那么最好将该操作包含在您的测试场景中,例如:
Given I have navigated to the UserDetails page
Given I enter <UserID>, <Name>,
Given I click on Create button
When I have navigated to the StudentCourse page
Then the user ID and name on that page will be <UserID>, <Name>
您好,我是 Specflow 和编码的新手。
我试图在一些全局声明的参数(变量)中存储一个值。但是,一旦执行了第二个示例(在场景大纲中),参数 returns Null。我的所有参数都返回 null。
Examples:
| UserID | Name|
| 111001 | AS |
| 114028 | AK | //2nd Example
在每个场景大纲之后 运行 我需要存储一个 ID。我的功能文件中总共有 4 个示例:场景大纲。
我的专题文件
Scenario Outline: 8. GetUserNumber
Given I enter <UserID>, <Name>,
And I click on Create button
And I verify if<UserID>, <Name> is same as selected in previous page
And I store the UserNumber
And I click on back button
Examples:
| UserID | Name|
| 111001 | AS |
| 114028 | AK |
| 131001 | ASF|
| 134028 | AKA|
我的代码
public class User
{
string PUP1; //Global declared variable
string PUP2; //Global declared variable
public void GetUserNum()
{
string UserNumPath;
if (UserGrade== "G" || Age== "7")
{
UserNumPath= driver.FindElement(By.Id("UserId")).Text;
PUP1 = (UserNumPath);// assigning UserNumPath to PUP1
}
else if (UserGrade== "H" || Age== "4")
{
UserNumPath= driver.FindElement(By.Id("UserId")).Text;
PUP2= (UserNumPath); // assigning UserNumPath to PUP2
}
}
}
如何在每个场景大纲示例执行后保存 ID?
为了帮助说明我的答案,这里有一个非常简单的 class 这是我测试的主题:
public class Animal
{
public Animal(string name)
{
this.Name = name;
}
public string Name { get; }
public string Speak()
{
switch (this.Name)
{
case "cat":
return "meow";
case "dog":
return "woof";
case "cow":
return "moo";
default:
return "I don't know";
}
}
}
这是我的功能文件:
Feature: Animal
Scenario: Animals say the right thing
Given I am a "<AnimalName>"
When I speak
Then I say "<Says>"
Examples:
| AnimalName | Says |
| dog | woof |
| cat | meow |
| cow | moo |
以及该功能文件中测试步骤的实现:
using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Infrastructure;
using Xunit;
[Binding]
public class AnimalSteps
{
private readonly ISpecFlowOutputHelper outputHelper;
private Animal animal;
private string whatISaid;
private static int someStaticField;
public AnimalSteps(ISpecFlowOutputHelper outputHelper)
{
this.outputHelper = outputHelper;
someStaticField++;
this.outputHelper.WriteLine($"In constructor: someStaticField is now {someStaticField}");
SomeStaticClass.Counter++;
this.outputHelper.WriteLine($"In constructor: SomeStaticClass.Counter is now {SomeStaticClass.Counter}");
}
[Given(@"I am a ""(.*)""")]
public void Given_IAmA(string animalName)
{
this.animal = new Animal(animalName);
}
[When(@"I speak")]
public void When_ISpeak()
{
this.outputHelper.WriteLine($"Storing what the animal said. Previous value: '{this.whatISaid}'");
this.whatISaid = this.animal.Speak();
}
[Then(@"I say ""(.*)""")]
public void Then_ISay(string whatToSay)
{
Assert.Equal(whatToSay, this.whatISaid);
}
}
这个 class 值得一些解释。
首先,有一个名为 outputHelper
的私有 ISpecFlowOutputHelper
字段。不用太担心这个,它只是一种将我自己的消息写到测试 运行ner 显示测试结果的地方的一种方式(我正在使用 Visual Studio 2019 的测试浏览器,它写这些,连同 SpecFlow 的一些输出,到每个测试用例的测试详细信息摘要)。
然后还有两个私有实例字段,分别叫做animal
和whatISaid
。这些用于在 AnimalSteps
class 中的不同方法之间共享信息,并且因为它们是实例字段,所以它们特定于 AnimalSteps
class 的当前实例,即每次创建一个新的 AnimalSteps
对象时,它都有自己的这些字段值,完全独立于任何其他 AnimalSteps
对象中那些字段的值。因为它们是私有的,所以它们只能从 AnimalSteps
class 内部访问,所以它们不能真正称为全局。
还有一个名为 someStaticField
的私有静态字段。这也只能从 AnimalSteps
class 中访问,但因为它是静态的(与实例相反),所以它的值在 AnimalSteps
class 的所有实例之间共享。这也是为什么当引用其他字段时引用此字段没有前缀 this.
- 此字段不是 AnimalSteps
的 this
实例的成员,它是成员AnimalSteps
.
所以让我们看一下输出(这是它在 VS 测试资源管理器中的样子,根据您使用的测试 运行 不同,它看起来可能会有所不同):
Given I am a "cat"
-> In constructor: someStaticField is now 1
-> In constructor: SomeStaticClass.Counter is now 1
-> done: AnimalSteps.Given_IAmA("cat") (0.0s)
When I speak
-> Storing what the animal said. Previous value: ''
-> done: AnimalSteps.When_ISpeak() (0.0s)
Then I say "meow"
-> done: AnimalSteps.Then_ISay("meow") (0.0s)
Given I am a "cow"
-> In constructor: someStaticField is now 3
-> In constructor: SomeStaticClass.Counter is now 3
-> done: AnimalSteps.Given_IAmA("cow") (0.0s)
When I speak
-> Storing what the animal said. Previous value: ''
-> done: AnimalSteps.When_ISpeak() (0.0s)
Then I say "moo"
-> done: AnimalSteps.Then_ISay("moo") (0.0s)
Given I am a "dog"
-> In constructor: someStaticField is now 2
-> In constructor: SomeStaticClass.Counter is now 2
-> done: AnimalSteps.Given_IAmA("dog") (0.0s)
When I speak
-> Storing what the animal said. Previous value: ''
-> done: AnimalSteps.When_ISpeak() (0.0s)
Then I say "woof"
-> done: AnimalSteps.Then_ISay("woof") (0.0s)
这里有几点需要注意:
whatISaid
字段的值在每个测试用例中都是一个空字符串,直到它在该测试用例中被设置。这是因为它是一个实例字段,每个测试用例都使用AnimalSteps
. 的新实例
someStaticField
字段的值每次都不同。这是因为它在AnimalSteps
的所有实例之间共享,并且AnimalSteps
构造函数在每次创建新实例时将其加 1。- 我们可以从
someStaticField
的值中看出,测试用例的顺序是 运行 猫、狗、牛。这与它们在特征文件中出现的顺序(狗、猫、牛)以及它们在 VS 测试资源管理器中出现的顺序(猫、牛、狗)都不相同。
最后一点很重要,因为 SpecFlow 所做的是为您的代码生成单元测试,单元测试应该相互独立;您不能对它们 运行 的顺序做出任何假设,并且一个测试不应依赖于另一个已经 运行.
的测试我还没有提到 SomeStaticClass
。这是它的样子:
public static class SomeStaticClass
{
public static int Counter { get; set; }
}
这有点像静态 someStaticField
字段,只是它不是 AnimalSteps
class 私有的,您可以从应用程序的任何位置访问它及其成员,无需创建它的实例,然后不必跟踪该实例是什么。如果你真的想要全局声明的变量,那么静态 classes 可能是最好的方法。
我希望您的问题离题有助于理解范围以及在您的应用程序及其测试中可以从何处访问的内容。但要点是,尝试将值从一个单元测试传递到另一个单元测试并不是一个好主意,因为您无法确定它们 运行 的顺序。如果您的测试用例需要检查特定操作的结果,那么最好将该操作包含在您的测试场景中,例如:
Given I have navigated to the UserDetails page
Given I enter <UserID>, <Name>,
Given I click on Create button
When I have navigated to the StudentCourse page
Then the user ID and name on that page will be <UserID>, <Name>