私はスペックフローを学ぼうとしています。現在、2つの機能ファイルがあります。
2番目の機能ファイルでは、最初の機能ファイルのステップを再利用しています。
Specflowは最初の機能ファイルからのステップを自動的に認識し、specflowが2番目の機能のステップを生成したとき、それは賢く、再利用しているステップを再生成しませんでした。
ただし、このステップは指定されたステップであり、フィーチャクラスのメンバーフィールドを初期化します。
シナリオコンテキストを使用せずに、クラスのメンバーを初期化する別のフィーチャファイルのステップを再利用するにはどうすればよいですか?
たとえば、Given Iがログインしている場合、それはいくつかの機能ファイルで使用されます。この「Given」は、ログに記録されるユーザーオブジェクトを作成し、それを.cs機能ファイルのメンバーとして保存します。
同じGivenを別の.featureで使用すると、Specflowは対応する.csファイルでそれを再生成しません。それを使用しているシナリオをデバッグすると、最初の.csファイルから実行されます。
しかし、最初の.cs機能ファイルのメンバーにアクセスできません。静的メンバーを使用することを計画していますが、おそらく別の解決策がありますか?
どうもありがとう。
ここでの大きなポイントは、ステップBinding
sがグローバルであるということです。これは、多くの人が経験するSpecflowの一般的なアンチパターンのようです。最初に、機能ファイルに一致するバインディングクラスの階層を作成するフェーズがあります。代わりに、機能と一致しないコラボレーションクラスを作成する必要がありますが、代わりにそれらのコラボレーションを通じて機能を生成します。
メインのアプリケーションコードと同じです。単一のATMMachineCashWithdrawal
クラスはなく、代わりにATMMachine
、PINCodeCheck
、およびOperationSelection
を持つWithdrawalOperation
があります。これらのオブジェクトは連携して「現金を引き出したい」機能を作成し、「残高の確認」機能を追加すると、WithdrawalOperation
以外のすべてを再利用できます。
Specflowのバインディングは同じです。 ATMTester
を設定する方法を知っていてGiven I have a cash machine full of cash
を提供するATMMachine
があり、Given my account has loads of money in it
を使用してアカウントの残高を偽造/モック/設定する方法を知っているCustomerTester
がある場合があります。
幸い、SpecFlowはクラスを共同作業する方法も提供します。ご覧ください http://www.specflow.org/documentation/Sharing-Data-between-Bindings/
私はちょうど同じ問題を抱えていました。派生クラスの属性「Binding」を設定し、各クラスのスコープを設定する必要があります。
2つの機能があるとしましょう:
機能:私の2番目の機能
Feature: My First Feature
Background:
Given a precondition
When ...
Then ...
Feature: My Second Feature
Background:
Given a precondition
When ...
Then ...
共有動作を定義するBaseClassがあります
// no Binding attribute for the BaseClass
public class BaseClass
{
[Given(@"a precondition")]
public void GivenAPrecondition()
{
}
}
次に、2つの機能の動作を定義する2つのクラス
[Binding]
[Scope(Feature = "My First Feature")]
public class MyFirstFeature : BaseClass
{
}
[Binding]
[Scope(Feature = "My Second Feature")]
public class MySecondFeature : BaseClass
{
}
私が行ったことの1つは、単一の大規模なpartial class
さまざまな* .csファイルに分割します。
これにより、関連するものを独自のファイルに分離しておくことができますが、フィクスチャコードを再利用するための多くのオプションが提供されます。
例えば(Feature1Steps.cs)
namespace YourProject.Specs
{
[Binding] // This can only be used once.
public partial class YourProjectSpecSteps
{
// Feature 1 methods ...
}
}
そして次の機能(Feature2Steps.cs)のために
namespace YourProject.Specs
{
public partial class YourProjectSpecSteps // same class, already bound
{
// Feature 2 methods ...
}
}
2つの機能ファイルがあるとおっしゃっていましたが、シナリオで共通のステップを使用する2つのシナリオと、実装が異なる2つの同じ名前のステップで1つの機能ファイルを作成することも検討してください。 (オーバーロードされた関数)
Ex: Login.featue file
Feature: Login
Test the login functionality of the application.
Will verify if the username and password combination is working as expected.
Scenario: Verify if the login functionality is working
Given I have navigated to the application
# user name and password are hard coded
When I fill in my form
| username | password |
|[email protected] | pwd |
....
Scenario: Verify if the login functionality is working for sql server data
Given I have navigated to the application
# gets the user name and password from the database
When I fill in my form
....
LoginSteps.cs file
[Binding]
public class LoginSteps
{
[Given(@"I have navigated to the application")]
public void GivenIHaveNavigatedToTheApplication()
{
// this code is used by both scenarios
Browser.Navigate().GoToUrl(ConfigurationManager.AppSettings["TestUrl"]);
}
// hard coded username and password
[When(@"I fill in my form")]
public void WhenIFillInMyForm(Table table)
{
dynamic credentials = table.CreateDynamicInstance();
LoginFunction(credentials.username, credentials.password);
}
// gets the username and password from the database
[When(@"I fill in my form")]
public void WhenIFillInMyForm()
{
string username = "";
string password = "";
string sql = <select statement>;
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString = ConfigurationManager.ConnectionStrings["SQLProvider"].ConnectionString;
connection.Open();
SqlCommand myCommand = new SqlCommand(sql, connection);
using (SqlDataReader myDataReader = myCommand.ExecuteReader())
{
while (myDataReader.Read())
{
username = myDataReader["name"].ToString();
password = myDataReader["pwd"].ToString();
}
}
}
LoginFunction(username, password);
}