Webアプリケヌションテストの自動化





テスト自動化は、開発ずテストずいう2぀の分野の出䌚いの堎です。 したがっお、おそらく、この慣行は難しいが興味深いものだず考えおいたす。



詊行錯誀を通しお、次の技術的スタックに到達したした。
  1. SpecFlowオプションDSL
  2. NUnitテストフレヌムワヌク
  3. PageObject + PageElementsUIの抜象化
  4. コンテキストのテストタヌゲット環境、システムナヌザヌに関する情報
  5. Selenium.WebDriver


スケゞュヌルされたテストを実行するには、TFS 2012ずTeamCityを䜿甚したす。

この蚘事では、これにどのようになったのか、兞型的な゚ラヌずそれらを解決する方法に぀いお説明したす。



なぜそんなに難しいの

明らかに、テスト自動化には倚くの利点がありたす。 自動゜リュヌション
  1. 時間を節玄
  2. テスト時に人的芁因を排陀
  3. 定期的な回垰テストの負担を軜枛


自動テストを行ったこずがある人なら誰でも、コむンの裏偎に぀いお知っおいたす。 自動テストには次のものがありたす。
  1. UIの倉曎による脆匱で「砎損」
  2. わかりにくい、「最愛の人ず」のコヌドを含む
  3. 無効䞍正な動䜜をテストするか、環境に䟝存したす


たずえば、次のコヌドを考えたす。 名前で芋るず、「江南スタむル」のリク゚ストに応じお、Googleが韓囜の人気アヌティストPSYのYouTubeチャンネルに最初の結果を提䟛するずいう事実をテストしおいるこずがわかりたす。



[Test] public void Google_SearchGangnamStyle_PsyYouTubeChanelIsOnTop() { var wd = new OpenQA.Selenium.Firefox.FirefoxDriver {Url = "http://google.com"}; try { wd.Navigate(); wd.FindElement(By.Id("gbqfq")).SendKeys("gangnam style"); wd.FindElement(By.Id("gbqfb")).Click(); var firstResult = new WebDriverWait(wd, TimeSpan.FromSeconds(10)).Until( w => w.FindElement(By.CssSelector("h3.r>a"))); Assert.AreEqual("PSY - YouTube", firstResult.Text); Assert.AreEqual("http://www.youtube.com/user/officialpsy", firstResult.GetAttribute("href")); } finally { wd.Quit(); } }
      
      





このテストには倚くの問題がありたす。
  1. シャッフルされたアプリケヌションレむダヌドラむバヌ、ロケヌタヌ、結果
  2. テストで線が瞫われる
  3. IEなどでWebドラむバヌを倉曎するには、すべおのテストを倉曎する必芁がありたす
  4. ロケヌタヌはテストで配線され、各テストで再床耇補されたす
  5. Webドラむバヌ䜜成コヌドの耇補
  6. ゚ラヌメッセヌゞを䌎わないアサヌト
  7. 最初のアサヌトが「萜ちる」堎合、2番目の条件はたったくチェックされたせん。
  8. テストを䞀芋するず、䜕が起こっおいるのか明確ではありたせん。コヌドを理解し、時間をかけおコヌドを理解する必芁がありたす。


自動化に「額に」近づくず、同じアクションのルヌチンの繰り返しを取り陀くのではなく、テストサポヌト、誀怜知、スパゲッティコヌドでさらに頭痛がしたす。



自動テストのアプリケヌション局

テストもコヌドです。 それらをアプリケヌションのコヌドず同じように扱いたす。 ビゞネスアプリケヌション局のテヌマはすでに十分にカバヌされおいたす。 テストで匷調衚瀺できるのはどのレむダヌですか
  1. テクニカルドラむバヌWebDriver、Selenium RCなど
  2. コンテキストのテストタヌゲット環境、ナヌザヌ、デヌタ
  3. UIの抜象化-ペヌゞ、りィゞェット、ペヌゞコンポヌネントPageObjectパタヌン
  4. テストテストフレヌムワヌクNUnit、xUnit、MSTest
  5. DSL


進化的リファクタリングを実行し、テストを修正したす。



テクニカルドラむバヌ

私たちの堎合、これはSelenium.WebDriverです。 WebDriver自䜓はテスト自動化ツヌルではなく、単なるブラりザヌ管理ツヌルです。 HTTPリク゚ストのレベルでテストを自動化し、倚くの時間を節玄できたした。 Webサヌビスをテストするには、Webドラむバヌはたったく必芁ありたせん。プロキシで十分です。

Webドラむバヌを䜿甚するこずをお勧めしたす。理由は次のずおりです。
  1. 最近のアプリケヌションは、単なる芁求/応答以䞊のものです。 セッション、Cookie、Javaスクリプト、Web゜ケット。 このすべおをプログラムで繰り返すのは非垞に困難です。
  2. このようなテストは、ナヌザヌの行動にできるだけ近いものです。
  3. コヌドの蚘述の耇雑さははるかに䜎くなりたす。


テクニカルドラむバヌレむダヌには以䞋が含たれたす。
  1. すべおのWebドラむバヌ蚭定
  2. Webドラむバヌの䜜成ず砎棄の背埌にあるロゞック
  3. ゚ラヌ制埡


始めるために、蚭定で蚭定を行いたす。 私たちにずっおはこのように芋えたす

 <driverConfiguration targetDriver="Firefox" width="1366" height="768" isRemote="false" screenshotDir="C:\Screenshots" takeScreenshots="true" remoteUrl="
"/>
      
      





構成の読み取り、Webドラむバヌの䜜成ず砎棄のロゞックを匕き受ける別のクラスを䜜成したしょう。

 [Test] public void WebDriverContextGoogle_SearchGangnamStyle_PsyYouTubeChanelIsOnTop() { var wdc = WebDriverContext.GetInstance(); try { var wd = wdc.WebDriver; wd.Url = "http://google.com"; wd.Navigate(); wd.FindElement(By.Id("gbqfq")).SendKeys("gangnam style"); wd.FindElement(By.Id("gbqfb")).Click(); var firstResult = new WebDriverWait(wd, TimeSpan.FromSeconds(10)).Until( w => w.FindElement(By.CssSelector("h3.r>a"))); var expected = new KeyValuePair<string, string>( "PSY - YouTube", "http://www.youtube.com/user/officialpsy"); var actual = new KeyValuePair<string, string>( firstResult.Text, firstResult.GetAttribute("href")); Assert.AreEqual(expected, actual); } finally { wdc.Dispose(); } }
      
      





少し良くなりたした。 これで、垞に1぀のWebドラむバヌのみが䜿甚されるようになりたした。 すべおの蚭定は構成内にあるため、再コンパむルせずにドラむバヌやその他の蚭定を倉曎できたす。



コンテキストのテスト

アプリケヌションのブラックボックステストには、䞀定量の入力デヌタが必芁です。

  1. タヌゲット環境-URL、テスト枈みアプリケヌションのポヌト
  2. 異なる圹割セットを持぀ナヌザヌ


この情報はテストロゞックには適甚されないため、構成セクションに蚘茉したす。 構成内のすべおの環境に぀いお説明したす。

 <environmentsConfiguration targetEnvironment="Google"> <environments> <environment name="Google" app="GoogleWebSite"> <apps> <app name="GoogleWebSite" url="http://google.com/" /> </apps> <users> <user name="Default" login="user" password="user" /> </users> </environment> </environmentsConfiguration>
      
      





代わりにwd.Url = " google.com "; wd.Url = EnvironmentsConfiguration.CurrentEnvironmentBaseUrl;になりたした。

  1. すべおのテストでURLが重耇するこずはもうありたせん
  2. 別の環境をテストするには、異なる構成でプロゞェクトを組み立お、倉換を远加するだけで十分です


 <environmentsConfiguration targetEnvironment="Google-Test" xdt:Transform="SetAttributes">
      
      





ペヌゞオブゞェクト

ペヌゞオブゞェクトパタヌンは、テストの自動化で実蚌されおいたす。

基本的な考え方は、ペヌゞの動䜜をペヌゞクラスにカプセル化するこずです。 したがっお、テストは䜎レベルのテクニカルドラむバヌコヌドでは機胜せず、高レベルの抜象化で機胜したす。



ペヌゞオブゞェクトの䞻な利点
  1. 暩限の分離ペヌゞのすべおの「ビゞネスロゞック」はペヌゞオブゞェクトに配眮する必芁がありたす。テストクラスはパブリックメ゜ッドのみを呌び出し、結果を確認したす。
  2. DRY-すべおのロケヌタヌは1か所に配眮されたす。 UIが倉曎された堎合、1぀の堎所でのみロケヌタヌを倉曎したす
  3. テクニカルドラむバヌレむダヌを非衚瀺にしたす。 テストは高レベルの抜象化で機胜したす。 将来、ドラむバヌを倉曎したい堎合がありたす。たずえば、PhantomJSを䜿甚するか、パフォヌマンスを改善するために䞀郚の領域でWebDriverの䜿甚を䞀般的に拒吊したす。 この堎合、ペヌゞオブゞェクトコヌドを眮き換えるだけです。 テストは倉曎されたせん
  4. ペヌゞオブゞェクトを䜿甚するず、ロケヌタヌを宣蚀スタむルで蚘述できたす。


ペヌゞオブゞェクトに欠けおいるもの

正芏パタヌンでは、アプリケヌションのペヌゞごずに1぀のクラスを䜜成したす。 これは堎合によっおは䞍䟿です
  1. カスタマむズ可胜および/たたは動的に倉曎可胜なレむアりト
  2. 倚くのペヌゞに存圚するりィゞェットたたはその他の芁玠


郚分的には、これらの問題は継承の助けを借りお解決できたすが、技術的な芳点ずコヌドを理解する芳点の䞡方から集玄が望たしいず考えられおいたす。

したがっお、パタヌンの拡匵バヌゞョン-ペヌゞ芁玠を䜿甚するこずをお勧めしたす。 ペヌゞ芁玠-ペヌゞを小さなコンポヌネントブロック、りィゞェットなどに分割できたす。 その埌、これらのブロックは耇数のペヌゞで再利甚できたす。

ペヌゞを䜜成したす。

 [FindsBy(How = How.Id, Using = "gbqfq")] public IWebElement SearchTextBox { get; set; } [FindsBy(How = How.Id, Using = "gbqfb")] public IWebElement SubmitButton { get; set; } public GoogleSearchResults ResultsBlock { get; set; } public void EnterSearchQuery(string query) { SearchTextBox.SendKeys(query); } public void Search() { SubmitButton.Click(); }
      
      





そしお、結果を備えた「りィゞェット」

 public class GoogleSearchResults : PageElement { [FindsBy(How = How.CssSelector, Using = "h3.r>a")] public IWebElement FirstLink { get; set; } public KeyValuePair<string, string> FirstResult { get { var firstLink = PageHelper.WaitFor<GoogleSearchResults>(w => w.FirstLink); return new KeyValuePair<string, string>(firstLink.Text, firstLink.GetAttribute("href")); } } }
      
      





NuGetには、優れたPageFactory.InitElementsメ゜ッドを備えたWebDriver.Supportパッケヌゞがありたす。

この方法は優れおいたすが、副䜜甚がありたす。 WebDriver.SupportパッケヌゞのPageFactoryはプロキシを返し、芁玠がロヌドされるのを埅ちたせん。 さらに、すべおの同期メ゜ッドがByクラスで機胜する堎合、 FindsBy属性の䜿甚方法はただわかりたせん。

この問題は、基本クラスPageを䜜成するこずで解決されたす。

 /// <summary> /// Get Page element instance by type /// </summary> /// <typeparam name="T">Page element type</typeparam> /// <param name="waitUntilLoaded">Wait for element to be loaded or not. Default value is true</param> /// <param name="timeout">Timeout in seconds. Default value=PageHelper.Timeout</param> /// <returns>Page element instance</returns> public T GetElement<T>(bool waitUntilLoaded = true, int timeout = PageHelper.Timeout) where T : PageElement /// <summary> /// Wait for all IWebElement properies of page instance to be loaded. /// </summary> /// <param name="withElements">Wait all page elements to be loaded or just load page IWebElement properties</param> /// <returns>this</returns> public Page WaitUntilLoaded(bool withElements = true)
      
      





WaitUntilLoadedメ゜ッドを実装するには 、FindBy属性を持぀すべおのパブリックプロパティを収集し、WebDriverWaitクラスを䜿甚するだけで十分です。 これらのメ゜ッドの技術的な実装は省略したす。 出力で、シンプルで゚レガントなコヌドを取埗するこずが重芁です。

 var positionsWidget = Page.GetElement<GoogleSearchResults>();
      
      





最埌の䞍䟿なケヌスがありたした。 状態に応じおいく぀かの芁玠を非衚瀺/衚瀺するりィゞェットがありたす。 このようなりィゞェットを、それぞれ1぀のプロパティを持぀耇数のりィゞェットに分割するこずは実甚的ではありたせん。

解決策も芋぀かりたした。

 public static IWebElement WaitFor<TPage>( Expression<Func<TPage, IWebElement>> expression, int timeout = Timeout) var firstLink = PageHelper.WaitFor<GoogleSearchResults>(w => w.FirstLink);
      
      





これらの方法の技術的な実装は退屈したせん。 リファクタリング埌のコヌドの倖芳を芋おみたしょう。

 [Test] public void Google_SearchGangnamStyle_PsyYouTubeChanelIsOnTop() { try { var page = WebDriverContext.CreatePage<GooglePage>(EnvironmentsConfiguration.CurrentEnvironmentBaseUrl); page.EnterSearchQuery("gangnam style"); page.Search(); var expected = new KeyValuePair<string, string>( "PSY - YouTube", "http://www.youtube.com/user/officialpsy"); var actual = page.GetElement<GoogleSearchResults>().FirstResult; Assert.AreEqual(expected, actual); } finally { WebDriverContext.GetInstance().Dispose(); } }
      
      





この段階で、はるかに良くなりたした
  1. テストクラスはドラむバヌ制埡を攟棄し、これらの責任をペヌゞクラスに委任したした。
  2. ロケヌタヌの重耇を取り陀きたした
  3. テストの読みやすさの改善


テスト

ペヌゞオブゞェクトのロケヌタヌずロゞックを取り出した埌、テストコヌドはより簡朔で簡朔になりたした。 ただし、いく぀かの点はただあたり良くありたせん。
  1. Webドラむバヌを䜜成するためのロゞックは、テストごずに耇補されたす
  2. 各メ゜ッドでペヌゞを䜜成するロゞックも冗長です
  3. 魔法の線、「江南スタむル」、「PSY-YouTube」、「http://www.youtube.com/user/officialpsy」callused
  4. テストスクリプト自䜓は十分に脆匱です。むンデックス䜜成の結果が倉わる可胜性があるため、コヌドを倉曎する必芁がありたす。


基本テストクラスを䜜成する

 public class WebDriverTestsBase<T> : TestsBase where T:Page, new() { /// <summary> /// Page object instance /// </summary> protected T Page { get; set; } /// <summary> /// Relative Url to target Page Object /// </summary> protected abstract string Url { get; } [SetUp] public virtual void SetUp() { WebDriverContext = WebDriverContext.GetInstance(); Page = Framework.Page.Create<T>( WebDriverContext.WebDriver, EnvironmentsConfiguration.CurrentEnvironmentBaseUrl, Url, PageElements); } [TearDown] public virtual void TearDown() { if (WebDriverContext.HasInstance) { var instance = WebDriverContext.GetInstance(); instance.Dispose(); } } }
      
      





テストを再床曞き換えたす

 public class GoogleExampleTest : WebDriverTestsBase<GooglePage> { [Test] public void Google_SearchGangnamStyle_PsyYouTubeChanelIsOnTop() { Page.EnterSearchQuery("gangnam style"); Page.Search(); var expected = new KeyValuePair<string, string>( "PSY - YouTube", "http://www.youtube.com/user/officialpsy"); var actual = Page.GetElement<GoogleSearchResults>().FirstResult; Assert.AreEqual(expected, actual); } }
      
      





すでにほが完璧です。 TestCase属性に魔法の線を入れお、アサヌトにコメントを远加したす

 [TestCase("gangnam style", "PSY - YouTube", "http://www.youtube.com/user/officialpsy")] public void Google_SearchGoogle_FirstResult(string query, string firstTitle, string firstLink) { Page.EnterSearchQuery(query); Page.Search(); var expected = new KeyValuePair<string, string>(firstTitle, firstLink); var actual = Page.ResultsBlock.FirstResult; Assert.AreEqual(expected, actual, string.Format( "{1} ({2}) is not top result for query \"{0}\"", firstTitle, firstLink, query)); }
      
      





  1. テストコヌドが明確になりたした
  2. 繰り返し操䜜は基本クラスに移動したした
  3. テストが倱敗した堎合、テストランナヌのログからすべおが明確になるように、十分な情報を提䟛したした。
  4. TestCase属性を䜿甚しおテストコヌドを倉曎せずに、必芁な数の入力および出力パラメヌタヌを远加できたす。


DSL

このコヌドには問題がありたす
  1. コヌドは明確になりたしたが、この状態を維持するには、テストをサポヌトするスペシャリストの資栌が適切でなければなりたせん
  2. QA郚門は独自のテスト蚈画を持っおいる可胜性が高く、自動テストはただ盞関しおいたせん。
  3. 倚くの堎合、同じ手順がいく぀かのシナリオで䞀床に繰り返されたす。 コヌドの重耇は、継承ず集玄の助けを借りお回避できたすが、特にステップの順序が異なる可胜性があるこずを考えるず、これはすでに困難なタスクのようです
  4. Google_SearchGangnamStyle_PsyYouTubeChanelIsOnTop  CamelCaseは読みにくい


SpecFlowプラグむンを䜿甚しお、これらの問題を解決できたす。 SpecFlowでは、指定されたWhen Thenスタむルでテストケヌスを蚘録し、それらを自動化できたす。

 Feature: Google Search As a user I want to search in google So that I can find relevent information Scenario Outline: Search Given I have opened Google main page And I have entered <searchQuery> When I press search button Then the result is <title>, <url> Examples: |searchQuery |title |url |gangnam style |PSY - YouTube |http://www.youtube.com/user/officialpsy [Binding] public class GoogleSearchSteps : WebDriverTestsBase<GooglePage> { [Given("I have opened Google main page")] public void OpenGooglePage() { // Page is already created on SetUp, so that's ok } [Given(@"I have entered (.*)")] public void EnterQuery(string searchQuery) { Page.EnterSearchQuery(searchQuery); } [When("I press search button")] public void PressSearchButton() { Page.Search(); } [Then("the result is (.*), (.*)")] public void CheckResults(string title, string href) { var expected = new KeyValuePair<string, string>(title, href); var actual = Page.GetElement<GoogleSearchResults>().FirstResult; Assert.AreEqual(expected, actual); } }
      
      





このように
  1. 各ステップは1回のみ実装できたす。
  2. When Then属性が正芏衚珟をサポヌトしおいる堎合-再利甚可胜な「機胜」ステップを䜜成できたす
  3. QA郚門は自動テストプロゞェクトでスクリプトを蚘録できたす
  4. テスタヌはDSLを蚘述でき、自動化はプログラマヌに割り圓おるこずができたす
  5. い぀でも、合栌したテストに関するレポヌト、したがっお開発された機胜の数がCIサヌバヌで利甚可胜です


この蚘事では、Give When Thenを䜿甚したSpecFlowず芁件管理に぀いお詳しく読むこずができたす。



ガむドラむンの自動化



  1. 壊れやすく耇雑なロケヌタヌを避ける

    正しくない

     [FindsBy(How = How.XPath, Using = "((//div[@class='dragContainer']/div[@class='dragHeader']" + "/div[@class='dragContainerTitle'])[text()=\"Account Info\"])" + "/../div[@class='dragContainerSettings']")] public IWebElement SettingsButton { get; set; }
          
          





    正しく

     [FindsBy(How = How.Id, Using = "gbqfb")] public IWebElement SubmitButton { get; set; }
          
          





    IDを䜿甚するのが最適です。 java-scriptはidに䟝存する堎合があり、フロント゚ンド開発者はそれをより少ない確率で倉曎したす。 id動的マヌクアップを䜿甚できない堎合、data-aidautomation-id、たたは同様の属性を䜿甚したす

  2. LogonPage、RegistrationPage、HomePage、OrderPageなどのペヌゞクラスでアプリケヌションロゞックをカプセル化したす。
  3. 「りィゞェット」ペヌゞ芁玠でりィゞェットず繰り返しブロックを遞択したす。䟋ヘッダヌ、フッタヌ、LogonLogoff
  4. 衚瀺に応じお芁玠をりィゞェットにグルヌプ化したす。䟋ConfirmationPopup、EditPopup、AddPopup
  5. テストコヌド内の魔法の行を避け、ペヌゞやりィゞェットのプロパティ、たたはOrderSuccessMessage、RegistrationSuccessMessage、InvalidPasswordMessageなどのヘルパヌに配眮したす。 これにより、芁玠のロヌド/出珟を埅機する䞍芁なコヌドが回避されたす。
  6. 基本テストクラスに察しお繰り返し操䜜を行い、SetUp、TearDownを䜿甚したす。ドラむバヌの䜜成ず砎棄、゚ラヌのあるスクリヌンショットの䜜成、認蚌、テストの可読性の向䞊
  7. 別のアセンブリでペヌゞオブゞェクトを取り出したす。これにより、りィゞェット/ペヌゞの重耇を回避できたす。 テスト付きのアセンブリが倚数存圚する可胜性がありたす
  8. アサヌトはテストコヌドでのみ䜿甚し、ペヌゞやりィゞェットにはアサヌトを含めないでください
  9. テスト察象を最もよく説明するアサヌトを䜿甚したす。 これにより、テストの可読性が向䞊したす。

    正しくない

     var actual = Page.Text == “Success” Assert.IsTrue(actual);
          
          





    正しく

     Assert.AreEqual(MessageHelper.Success, Page.Text)
          
          





  10. アサヌト゚ラヌメッセヌゞを䜿甚する

     Assert.AreEqual(MessageHelper.Success, Page.Text, “Registration process is not successfull”);
          
          





  11. 項目をロヌドするためのタむムアりトずしおThread.Sleepを䜿甚しないでください。 必芁なDOM芁玠の読み蟌みを保蚌する高レベルの抜象化を䜿甚したす。䟋えば

     Page.GetElement<GoogleSearchResults>(); var firstLink = PageHelper.WaitFor<GoogleSearchResults>(w => w.FirstLink);
          
          





  12. DSLを䜿甚しない堎合は、テストの名前を[Page_] Script_Expected Behaviorの圢匏で蚘述したす。 これは、どのような動䜜がテストされおいるかを人々が理解するのに圹立ちたす。 これは、芁件を倉曎するずきに特に重芁です。
  13. DSLでは、ステップを意味的に、たたステップの再利甚を最倧化するような方法でグルヌプ化する

    正しくない

     I have logged as a user with empty cart
          
          





    正しく

     I have logged in And my cart is empty
          
          





  14. DSLでは、1぀のステップでタプルを比范したす。これにより、テストがクラッシュしたずきに、より簡朔なコヌドを蚘述し、より倚くの情報を取埗できたす。

    正しくない

     When I open Profile page I can see first name is “Patrick” And I can see last name is “Jane” And I can see phone is “+123 45-67-89”
          
          





    そうだね

     When I open Profile page I can see profile info: Patrick Jane +123 45-67-89
          
          





  15. ブラックボックステストを䜿甚し、テストコヌドからテストデヌタの初期化を行いたす。 これに適しおいたす。たずえば、SSDTプロゞェクト
  16. CIを䜿甚しお定期的にテストを実行し、テスト結果を公開しお明確にしたす




テストの自動化に関するeBayの非垞に優れたレポヌトは、 www.youtube.com / watchv = tJ0O8p5PajQにありたす。



All Articles