如何使步骤参数依赖于配置?
How to make step argument dependent on configuration?
问题
我正在使用 SpecFlow 为 REST 服务创建集成测试套件。
我是 运行 多个不同配置的套件。 (我有多个构建配置,每个配置都有自己的一组 app.config 转换。)
在 C# 代码中,检查配置并根据它执行不同的代码非常简单。我可以简单地做这样的事情。
[Given(@"I set the test parameter to ""(.*)""")]
public void GivenISetTheTestParameter(string parameter)
{
if(CurrentConfiguration == Config.Test)
this.testParameter = parameter + "Test";
else if(CurrentConfiguration == Config.Prod)
this.testParameter = parameter + "Prod";
}
这种方法的问题是它在每次执行此步骤时都以相同的方式工作,但我不想在每个场景中以不同方式参数化步骤的 configuration-dependent 部分。
在 feature 文件中有什么方法可以做到这一点吗?我想做这样的事情(pseudo-code,这当然行不通):
If (CurrentConfiguration == Config.Test)
Given I set the test parameter to "ParameterTest"
Else If (CurrentConfiguration == Config.Prod)
Given I set the test parameter to "ParameterProd"
然后我可以在每个场景中以不同的方式使用这个参数化:
Scenario: Test 1
If (CurrentConfiguration == Config.Test)
Given I set the test parameter to "ParameterTest1"
Else If (CurrentConfiguration == Config.Prod)
Given I set the test parameter to "ParameterProd1"
...
Scenario: Test 2
If (CurrentConfiguration == Config.Test)
Given I set the test parameter to "ParameterTest2"
Else If (CurrentConfiguration == Config.Prod)
Given I set the test parameter to "ParameterProd2"
...
如果条件是在步骤的 C# 代码中实现的,则这是不可能的。
现实世界的例子
我想用它来集成测试 REST 服务。假设我使用基本身份验证,为此我需要在 RestClient
object.
上设置 header
我有一个帮助步骤,用于将身份验证 header 设置为特定的用户名和密码。
棘手的部分是我有多个构建配置(比方说 Staging 和 Prod),为此我需要不同的测试凭据。此外,我在我的功能的不同场景中调用不同的 API,这也需要不同的凭据。
所以通过上面的介绍pseudo-syntax,我想做的是:
Scenario: Test LoggingService
If (CurrentConfiguration == Config.Test)
Given I set the auth header for the user "logging_test_user" and password "p4ssword"
Else If (CurrentConfiguration == Config.Prod)
Given I set the auth header for the user "logging_prod_user" and password "p4ssword"
...
When I call the LoggingService
...
Scenario: Test PaymentService
If (CurrentConfiguration == Config.Test)
Given I set the auth header for the user "payment_test_user" and password "p4ssword"
Else If (CurrentConfiguration == Config.Prod)
Given I set the auth header for the user "payment_prod_user" and password "p4ssword"
...
When I call the PaymentService
...
如果我只能把条件放在"Given I set the auth header..."步骤的C#实现中,那么我就不能为不同的场景指定不同的用户名了。
我们对不同的环境做了类似的事情,但是我们有一个 app.config 用于测试,它有几个 'alternate' dev、qa 和 uat 配置,我们从中读取命名参数的值这些部分之一。
我们有这样的东西
<testingEnvironments>
<testingEnvironment name="Dev" messageUrl="https://somedevurl/" isCurrent="true">
<ConfigParam1>SomeValue</ConfigParam1>
</testingEnvironment>
<testingEnvironment name="QA" messageUrl="https://somedqaurl/" isCurrent="false">
<ConfigParam1>SomeValueInQA</ConfigParam1>
</testingEnvironment>
<testingEnvironment name="UAT" messageUrl="https://someuaturl/" isCurrent="false">
<ConfigParam1>SomeValueUAT</ConfigParam1>
</testingEnvironment>
</testingEnvironments>
我们 select 配置基于 isCurrent
属性的值,但您可以 select 基于环境变量的名称。
然后你的测试不知道所使用的确切值然后只参考 ConfigParam1
根据您的真实示例,我不喜欢测试中的实现细节(如果您使用其他身份验证机制会怎么样)并且会像这样重组我的规范:
Scenario: Test LoggingService
Given I am the logging service default user for the current environment
When I call the LoggingService
...
Scenario: Test payment Service
Given I am the payment service default user for the current environment
When I call the PaymentService
...
并且会添加这样的配置:
<testingEnvironment name="DEV" messageUrl="https://somedevurl/" isCurrent="false">
<userCredentials>
<LoggingService>
<defaultUser name="logging_test_user" password="p4ssword" />
</LoggingService>
<PaymentService>
<defaultUser name="payment_test_user" password="p4ssword" />
</PaymentService>
</userCredentials>
</testingEnvironment>
<testingEnvironment name="UAT" messageUrl="https://someuaturl/" isCurrent="false">
<userCredentials>
<LoggingService>
<defaultUser name="logging_prod_user" password="p4ssword" />
</LoggingService>
<PaymentService>
<defaultUser name="payment_prod_user" password="p4ssword" />
</PaymentService>
</userCredentials>
</testingEnvironment>
然后您的各个步骤可以调用通用步骤来设置实际的 header 值
您根本不需要功能文件中的可配置数据。相反,创建一个通用步骤,其定义读取配置文件:
Scenario: Test LoggingService
Given I set the auth header
在 C# 中:
[Given(@"I set the auth header")]
public void GivenISetTheAuthHeader()
{
string username = System.Configuration.ConfigurationManager.AppSettings["RestServiceUserName"];
string password = System.Configuration.ConfigurationManager.AppSettings["RestServicePassword"];
}
并且在 App.config 中:
<appSettings>
<add key="RestServiceUserName" value="..."/>
<add key="RestServicePassword" value="..."/>
如果不同的用户名在系统中具有不同的权限,那么请考虑改用场景大纲:
Scenario Outline: Testing the LoggingService
Given I set the auth header for user "<Username>" and password "<Password>"
Examples:
| Username | Password |
| user1 | pass1 |
| user2 | pass2 |
并且它们成为您的步骤定义的正常参数:
[Given("I set the auth header for user """(.*)""" and password """(.*)"""")]
public void GivenISetTheAuthHeaderForUserAndPassword(string username, string password)
{
// set the user and password on the auth header
}
您的测试应该始终相同 -- 带有 "if" 的测试至少是两个测试。解决这个问题的正确方法是隔离被测系统,使其采用表示配置值的参数(或以其他方式提供的值),然后为所有适用场景编写测试。
我会写这个功能:
Scenario: Test LoggingService
Given I set the auth header with valid user and password
When I call the LoggingService
# ...
设置App.config
文件:
<appSettings>
<add key="EnvironmentUserName" value="..."/>
<add key="EnvironmentPassword" value="..."/>
<!-- ... -->
</appSettings>
并将步骤实施为:
public void GivenISetTheAuthHeader()
{
string username = System.Configuration.ConfigurationManager.AppSettings["EnvironmentUserName"];
string password = System.Configuration.ConfigurationManager.AppSettings["EnvironmentPassword"];
// ...
}
您可以通过场景大纲和标记示例实现您想要的,但是您将不得不 运行 在某些环境中仅进行一些测试:
Scenario Outline: Testing the LoggingService
Given I set the auth header for user "<Username>" and password "<Password>"
@Production
Examples:
| Username | Password |
| logging_prod_user | p4ssword |
@Test
Examples:
| Username | Password |
| logging_test_user | p4assword |
然后将您的测试 运行 配置为 运行 仅测试某些类别(Test
或 Production
)
如果您使用 nunit(或 XUnit 或任何其他默认使用 运行ning 场景大纲的行测试的测试 运行ner)作为您的测试 运行ner,知道 this issue
问题
我正在使用 SpecFlow 为 REST 服务创建集成测试套件。
我是 运行 多个不同配置的套件。 (我有多个构建配置,每个配置都有自己的一组 app.config 转换。)
在 C# 代码中,检查配置并根据它执行不同的代码非常简单。我可以简单地做这样的事情。
[Given(@"I set the test parameter to ""(.*)""")]
public void GivenISetTheTestParameter(string parameter)
{
if(CurrentConfiguration == Config.Test)
this.testParameter = parameter + "Test";
else if(CurrentConfiguration == Config.Prod)
this.testParameter = parameter + "Prod";
}
这种方法的问题是它在每次执行此步骤时都以相同的方式工作,但我不想在每个场景中以不同方式参数化步骤的 configuration-dependent 部分。
在 feature 文件中有什么方法可以做到这一点吗?我想做这样的事情(pseudo-code,这当然行不通):
If (CurrentConfiguration == Config.Test)
Given I set the test parameter to "ParameterTest"
Else If (CurrentConfiguration == Config.Prod)
Given I set the test parameter to "ParameterProd"
然后我可以在每个场景中以不同的方式使用这个参数化:
Scenario: Test 1
If (CurrentConfiguration == Config.Test)
Given I set the test parameter to "ParameterTest1"
Else If (CurrentConfiguration == Config.Prod)
Given I set the test parameter to "ParameterProd1"
...
Scenario: Test 2
If (CurrentConfiguration == Config.Test)
Given I set the test parameter to "ParameterTest2"
Else If (CurrentConfiguration == Config.Prod)
Given I set the test parameter to "ParameterProd2"
...
如果条件是在步骤的 C# 代码中实现的,则这是不可能的。
现实世界的例子
我想用它来集成测试 REST 服务。假设我使用基本身份验证,为此我需要在 RestClient
object.
上设置 header
我有一个帮助步骤,用于将身份验证 header 设置为特定的用户名和密码。
棘手的部分是我有多个构建配置(比方说 Staging 和 Prod),为此我需要不同的测试凭据。此外,我在我的功能的不同场景中调用不同的 API,这也需要不同的凭据。
所以通过上面的介绍pseudo-syntax,我想做的是:
Scenario: Test LoggingService
If (CurrentConfiguration == Config.Test)
Given I set the auth header for the user "logging_test_user" and password "p4ssword"
Else If (CurrentConfiguration == Config.Prod)
Given I set the auth header for the user "logging_prod_user" and password "p4ssword"
...
When I call the LoggingService
...
Scenario: Test PaymentService
If (CurrentConfiguration == Config.Test)
Given I set the auth header for the user "payment_test_user" and password "p4ssword"
Else If (CurrentConfiguration == Config.Prod)
Given I set the auth header for the user "payment_prod_user" and password "p4ssword"
...
When I call the PaymentService
...
如果我只能把条件放在"Given I set the auth header..."步骤的C#实现中,那么我就不能为不同的场景指定不同的用户名了。
我们对不同的环境做了类似的事情,但是我们有一个 app.config 用于测试,它有几个 'alternate' dev、qa 和 uat 配置,我们从中读取命名参数的值这些部分之一。
我们有这样的东西
<testingEnvironments>
<testingEnvironment name="Dev" messageUrl="https://somedevurl/" isCurrent="true">
<ConfigParam1>SomeValue</ConfigParam1>
</testingEnvironment>
<testingEnvironment name="QA" messageUrl="https://somedqaurl/" isCurrent="false">
<ConfigParam1>SomeValueInQA</ConfigParam1>
</testingEnvironment>
<testingEnvironment name="UAT" messageUrl="https://someuaturl/" isCurrent="false">
<ConfigParam1>SomeValueUAT</ConfigParam1>
</testingEnvironment>
</testingEnvironments>
我们 select 配置基于 isCurrent
属性的值,但您可以 select 基于环境变量的名称。
然后你的测试不知道所使用的确切值然后只参考 ConfigParam1
根据您的真实示例,我不喜欢测试中的实现细节(如果您使用其他身份验证机制会怎么样)并且会像这样重组我的规范:
Scenario: Test LoggingService
Given I am the logging service default user for the current environment
When I call the LoggingService
...
Scenario: Test payment Service
Given I am the payment service default user for the current environment
When I call the PaymentService
...
并且会添加这样的配置:
<testingEnvironment name="DEV" messageUrl="https://somedevurl/" isCurrent="false">
<userCredentials>
<LoggingService>
<defaultUser name="logging_test_user" password="p4ssword" />
</LoggingService>
<PaymentService>
<defaultUser name="payment_test_user" password="p4ssword" />
</PaymentService>
</userCredentials>
</testingEnvironment>
<testingEnvironment name="UAT" messageUrl="https://someuaturl/" isCurrent="false">
<userCredentials>
<LoggingService>
<defaultUser name="logging_prod_user" password="p4ssword" />
</LoggingService>
<PaymentService>
<defaultUser name="payment_prod_user" password="p4ssword" />
</PaymentService>
</userCredentials>
</testingEnvironment>
然后您的各个步骤可以调用通用步骤来设置实际的 header 值
您根本不需要功能文件中的可配置数据。相反,创建一个通用步骤,其定义读取配置文件:
Scenario: Test LoggingService
Given I set the auth header
在 C# 中:
[Given(@"I set the auth header")]
public void GivenISetTheAuthHeader()
{
string username = System.Configuration.ConfigurationManager.AppSettings["RestServiceUserName"];
string password = System.Configuration.ConfigurationManager.AppSettings["RestServicePassword"];
}
并且在 App.config 中:
<appSettings>
<add key="RestServiceUserName" value="..."/>
<add key="RestServicePassword" value="..."/>
如果不同的用户名在系统中具有不同的权限,那么请考虑改用场景大纲:
Scenario Outline: Testing the LoggingService
Given I set the auth header for user "<Username>" and password "<Password>"
Examples:
| Username | Password |
| user1 | pass1 |
| user2 | pass2 |
并且它们成为您的步骤定义的正常参数:
[Given("I set the auth header for user """(.*)""" and password """(.*)"""")]
public void GivenISetTheAuthHeaderForUserAndPassword(string username, string password)
{
// set the user and password on the auth header
}
您的测试应该始终相同 -- 带有 "if" 的测试至少是两个测试。解决这个问题的正确方法是隔离被测系统,使其采用表示配置值的参数(或以其他方式提供的值),然后为所有适用场景编写测试。
我会写这个功能:
Scenario: Test LoggingService
Given I set the auth header with valid user and password
When I call the LoggingService
# ...
设置App.config
文件:
<appSettings>
<add key="EnvironmentUserName" value="..."/>
<add key="EnvironmentPassword" value="..."/>
<!-- ... -->
</appSettings>
并将步骤实施为:
public void GivenISetTheAuthHeader()
{
string username = System.Configuration.ConfigurationManager.AppSettings["EnvironmentUserName"];
string password = System.Configuration.ConfigurationManager.AppSettings["EnvironmentPassword"];
// ...
}
您可以通过场景大纲和标记示例实现您想要的,但是您将不得不 运行 在某些环境中仅进行一些测试:
Scenario Outline: Testing the LoggingService
Given I set the auth header for user "<Username>" and password "<Password>"
@Production
Examples:
| Username | Password |
| logging_prod_user | p4ssword |
@Test
Examples:
| Username | Password |
| logging_test_user | p4assword |
然后将您的测试 运行 配置为 运行 仅测试某些类别(Test
或 Production
)
如果您使用 nunit(或 XUnit 或任何其他默认使用 运行ning 场景大纲的行测试的测试 运行ner)作为您的测试 运行ner,知道 this issue