1. 概要

この記事では、SerenityBDDの脚本パターンについて簡単に説明します。 これを読む前に、まずセレニティBDDの基本を読むことをお勧めします。 また、SerenityBDDとSpringの統合に関する記事も興味深いかもしれません。

Serenity BDDで導入された脚本は、チームがより堅牢で信頼性の高いテストを作成できるようにすることで、優れたテスト習慣と適切に設計されたテストスイートを促進することを目的としています。 これは、SeleniumWebDriverとページオブジェクトモデルに基づいています。 Seleniumの概要を読んだ場合、これらの概念はかなりおなじみのものであることがわかります。

2. Mavenの依存関係

まず、pom.xmlファイルに次の依存関係を追加しましょう。

<dependency>
    <groupId>net.serenity-bdd</groupId>
    <artifactId>serenity-junit</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>net.serenity-bdd</groupId>
    <artifactId>serenity-screenplay</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>net.serenity-bdd</groupId>
    <artifactId>serenity-screenplay-webdriver</artifactId>
    <version>1.4.0</version>
</dependency>

serenity-screenplayおよびserenity-screenplay-webdriverの最新バージョンは、Maven中央リポジトリーからフェッチできます。

脚本を実行するには、Webドライバーも必要です。ChromeDriverまたはMozilla-GeckoDriverのいずれかで実行できます。 この記事では、ChromeDriverを使用します。

WebDriverを有効にするには、次のプラグイン構成が必要です。 webdriver.chrome.driver の値は、MavenプロジェクトのChromeDriverバイナリファイルの相対パスである必要があります。

<plugin>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>2.20</version>
    <configuration>
        <systemProperties>
            <webdriver.chrome.driver>chromedriver</webdriver.chrome.driver>
        </systemProperties>
    </configuration>
</plugin>

3. WebDriverのサポート

WebDriver変数に@Managedアノテーションをマークすることで、SerenityにWebDriverインスタンスを管理させることができます。 Serenityは、各テストの開始時に適切なドライバーを開き、テストが終了するとシャットダウンします。

次の例では、ChromeDriverを起動し、Googleを開いて「baeldung」を検索します。 Eugenの名前が検索結果に表示されることを期待しています。

@RunWith(SerenityRunner.class)
public class GoogleSearchLiveTest {

    @Managed(driver = "chrome") 
    private WebDriver browser;

    @Test
    public void whenGoogleBaeldungThenShouldSeeEugen() {
        browser.get("https://www.google.com/ncr");

        browser
          .findElement(By.name("q"))
          .sendKeys("baeldung", Keys.ENTER);

        new WebDriverWait(browser, 5)https://www.baeldung.com/serenity-screenplay
          .until(visibilityOfElementLocated(By.cssSelector("._ksh")));

        assertThat(browser
          .findElement(By.cssSelector("._ksh"))
          .getText(), containsString("Eugen (Baeldung)"));
    }
}

@Managed のパラメーターを指定しない場合、この場合、SerenityBDDはFirefoxを使用します。 @Managed アノテーションでサポートされているドライバーの全リスト: firefox、chrome、iexplorer、htmlunit、phantomjs

IExplorerまたはEdgeでテストする必要がある場合は、 here(IEの場合)および here(Edgeの場合)からそれぞれWebドライバーをダウンロードできます。 Safari WebDriverは、MacOSの / usr / bin /safaridriverでのみ使用できます。

4. ページオブジェクト

セレニティページオブジェクトは、WebDriverページオブジェクトを表します。 PageObject は、再利用のためにWebDriverの詳細を非表示にします。

4.1. PageObjectを使用したリファクタリングの例

最初にPageObjectを使用して、要素の検索、検索、および結果の検証アクションを抽出することにより、以前のテストを改良しましょう。

@DefaultUrl("https://www.google.com/ncr")
public class GoogleSearchPageObject extends PageObject {

    @FindBy(name = "q") 
    private WebElement search;

    @FindBy(css = "._ksh") 
    private WebElement result;

    public void searchFor(String keyword) {
        search.sendKeys(keyword, Keys.ENTER);
    }

    public void resultMatches(String expected) {
        assertThat(result.getText(), containsString(expected));
    }
}

WebElementはHTML要素を表します。 インターフェイスのAPIを介してWebページを操作できます。 上記の例では、ページ内のWeb要素を見つける2つの方法を使用しました。要素名と要素のCSSクラスです。

タグ名で検索、リンクテキストで検索など、Web要素を検索するときに適用する方法は他にもあります。 詳細については、Seleniumガイドを参照してください。

WebElementWebElementFacadeに置き換えることもできます。これにより、Web要素を処理するためのより流暢なAPIが提供されます。

SerenityはJUnitテストのすべてのPageObjectフィールドを自動的にインスタンス化するため、前のテストをよりクリーンなテストに書き換えることができます。

@RunWith(SerenityRunner.class)
public class GoogleSearchPageObjectLiveTest {

    @Managed(driver = "chrome") 
    private WebDriver browser;

    GoogleSearchPageObject googleSearch;

    @Test
    public void whenGoogleBaeldungThenShouldSeeEugen() {
        googleSearch.open();

        googleSearch.searchFor("baeldung");

        googleSearch.resultMatches("Eugen (Baeldung)");
    }
}

これで、 GoogleSearchPageObject に変更を加えることなく、他のキーワードを使用して検索し、関連する検索結果を照合できます。

4.2. 非同期サポート

現在、多くのWebページが動的に提供またはレンダリングされています。 このような場合に対処するために、 PageObject は、要素のステータスを検査できる多くの豊富な機能もサポートしています。 要素が表示されているかどうかを確認するか、表示されるまで待ってから先に進むことができます。

resultMatches メソッドを拡張して、見たい要素が表示されるようにします。

public void resultMatches(String expected) {
    waitFor(result).waitUntilVisible();
    assertThat(result.getText(), containsString(expected));
}

あまり長く待つことを期待しない場合は、待機中のアクションのタイムアウトを明示的に指定できます。

public void resultMatches(String expected) {
    withTimeoutOf(5, SECONDS)
      .waitFor(result)
      .waitUntilVisible();
    assertThat(result.getText(), containsString(expected));
}

5. 脚本パターン

脚本パターンは、SOLID設計原則を自動検収試験に適用します。 脚本パターンの一般的な理解は、give_when_thenコンテキストで次のように説明できます。

  • give –いくつかのタスクを実行できるアクター
  • when アクタータスクを実行します
  • 次に– アクターは効果を確認し、結果を確認する必要があります

次に、前のテストシナリオを脚本パターンに当てはめましょう。Googleを使用できるユーザーKittyが、Googleで「baeldung」を検索すると、Kittyは結果にEugenの名前を表示するはずです。

まず、キティが実行できるタスクを定義します。

  1. キティはGoogleを使用できます:
    public class StartWith implements Task {
    
        public static StartWith googleSearchPage() {
            return instrumented(StartWith.class);
        }
    
        GoogleSearchPage googleSearchPage;
    
        @Step("{0} starts a google search")
        public <T extends Actor> void performAs(T t) {
            t.attemptsTo(Open
              .browserOn()
              .the(googleSearchPage));
        }
    }
  2. キティはグーグルで検索することができます:
    public class SearchForKeyword implements Task {
    
        @Step("{0} searches for '#keyword'")
        public <T extends Actor> void performAs(T actor) {
            actor.attemptsTo(Enter
              .theValue(keyword)
              .into(GoogleSearchPage.SEARCH_INPUT_BOX)
              .thenHit(Keys.RETURN));
        }
    
        private String keyword;
    
        public SearchForKeyword(String keyword) {
            this.keyword = keyword;
        }
    
        public static Task of(String keyword) {
            return Instrumented
              .instanceOf(SearchForKeyword.class)
              .withProperties(keyword);
        }
    }
  3. キティはGoogleの検索結果を見ることができます:
    public class GoogleSearchResults implements Question<List<String>> {
    
        public static Question<List<String>> displayed() {
            return new GoogleSearchResults();
        }
    
        public List<String> answeredBy(Actor actor) {
            return Text
              .of(GoogleSearchPage.SEARCH_RESULT_TITLES)
              .viewedBy(actor)
              .asList();
        }
    }

また、Google検索PageObjectはすでに定義されています。

@DefaultUrl("https://www.google.com/ncr")
public class GoogleSearchPage extends PageObject {

    public static final Target SEARCH_RESULT_TITLES = Target
      .the("search results")
      .locatedBy("._ksh");

    public static final Target SEARCH_INPUT_BOX = Target
      .the("search input box")
      .locatedBy("#lst-ib");
}

これで、メインのテストクラスは次のようになります。

@RunWith(SerenityRunner.class)
public class GoogleSearchScreenplayLiveTest {

    @Managed(driver = "chrome") 
    WebDriver browser;

    Actor kitty = Actor.named("kitty");

    @Before
    public void setup() {
        kitty.can(BrowseTheWeb.with(browser));
    }

    @Test
    public void whenGoogleBaeldungThenShouldSeeEugen() {
        givenThat(kitty).wasAbleTo(StartWith.googleSearchPage());

        when(kitty).attemptsTo(SearchForKeyword.of("baeldung"));

        then(kitty).should(seeThat(GoogleSearchResults.displayed(), 
          hasItem(containsString("Eugen (Baeldung)"))));
    }
}

このテストを実行した後、テストレポートでキティが実行した各ステップのスクリーンショットが表示されます。

6. 概要

この記事では、SerenityBDDで脚本パターンを使用する方法を紹介しました。 また、 PageObject を使用すると、WebDriverと直接対話する必要がなくなり、テストの読み取り、保守、拡張が容易になります。

SerenityBDDのPageObjectと脚本パターンの詳細については、Serenityドキュメントの関連セクションを確認してください。

いつものように、完全なサンプルコードは、Githubにあります。