Serenity BDD and Screenplay – 宁静的BDD和剧本

最后修改: 2017年 6月 8日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll have a quick look at the Screenplay Pattern in Serenity BDD. We suggest you read the basics of Serenity BDD first before reading this one. Also, the article on Serenity BDD integration with Spring might also be interesting.

在这篇文章中,我们将快速了解Serenity BDD中的Screenplay模式。我们建议你在阅读这篇文章之前先阅读Serenity BDD的基础知识。另外,关于Serenity BDD与Spring的集成的文章可能也很有趣。

Screenplay, introduced in Serenity BDD, aims to encourage good testing habits and well-designed test suites by enabling teams to write more robust and reliable tests. It is based on the Selenium WebDriver and the Page Objects model. If you’ve read our introduction to Selenium, you’ll find these concepts rather familiar.

Screenplay,在Serenity BDD中引入,旨在鼓励良好的测试习惯和精心设计的测试套件,使团队能够编写更强大和可靠的测试。它是基于Selenium WebDriver和Page Objects模型。如果您读过我们的Selenium介绍,您会发现这些概念相当熟悉。

2. Maven Dependency

2.Maven的依赖性

First, let’s add the following dependencies to the pom.xml file:

首先,让我们在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>

The latest versions of serenity-screenplay and serenity-screenplay-webdriver can be fetched from the Maven Central Repository.

serenity-screenplayserenity-screenplay-webdriver的最新版本可以从Maven中央仓库获取。

We also need web drivers to perform screenplay – either ChromeDriver or Mozilla-GeckoDriver will do. In this article, we’ll use the ChromeDriver.

我们还需要网络驱动程序来执行屏幕播放 – ChromeDriverMozilla-GeckoDriver就可以了。在这篇文章中,我们将使用ChromeDriver。

The following plugin configuration is required to enable WebDriver, in which the value of webdriver.chrome.driver should be the relative path of ChromeDriver binary file in our maven project:

启用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 Support

3.WebDriver支持

We can make Serenity manage WebDriver instance by marking @Managed annotation on a WebDriver variable. Serenity will open an appropriate driver at the start of each test, and shut it down when the test is finished.

我们可以通过在WebDriver变量上标记@Managed注解使Serenity管理WebDriver实例。Serenity将在每个测试开始时打开一个适当的驱动程序,并在测试结束时关闭它。

In the following example, we initiate a ChromeDriver and opens Google to search for ‘baeldung’. We expect Eugen’s name to be present in the search results:

在下面的例子中,我们启动了ChromeDriver,并打开谷歌搜索 “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)"));
    }
}

If we don’t specify any parameters for @Managed, Serenity BDD will use Firefox in this case. The whole list of supported drivers by the @Managed annotation: firefox, chrome, iexplorer, htmlunit, phantomjs.

如果我们没有为@Managed指定任何参数,Serenity BDD将在这种情况下使用Firefox。@Managed注解所支持的驱动程序的整个列表。firefox, chrome, iexplorer, htmlunit, phantomjs

If we need to test in IExplorer or Edge, we can download web drivers from here(for IE) and here(for Edge) respectively. Safari WebDriver is only available on MacOS under /usr/bin/safaridriver.

如果我们需要在Explorer或Edge中测试,我们可以分别从这里(用于IE)这里(用于Edge)下载Web驱动程序。Safari WebDriver只在MacOS下/usr/bin/safaridriver提供。

4. Page Objects

4.页面对象

Serenity Page Objects represent a WebDriver page object. The PageObject hides WebDriver details for reuse.

Serenity Page Objects代表一个WebDriver页面对象。PageObject隐藏了WebDriver的细节,以便重复使用。

4.1. Refactor Example Using PageObject

4.1.使用PageObject的重构示例

Let’s refine our previous test using PageObject first by extracting element locating, searching and result verifying actions:

让我们先用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 represents an HTML element. We can interact with web pages through APIs of the interface. In the example above, we used two ways of locating web elements in the page: by element name and by element’s CSS classes.

WebElement代表一个HTML元素。我们可以通过界面的API与网页进行交互。在上面的例子中,我们使用了两种方式来定位网页中的元素:通过元素名称和元素的CSS类。

There are more approaches to applying when finding web elements, such as find by tag name, find by link text, etc. Refer to our guide to Selenium for more details.

在寻找网络元素时,还有更多的方法可以应用,如按标签名称查找、按链接文本查找等。请参考我们的Selenium指南以了解更多细节。

We can also replace WebElement with WebElementFacade, which provides more fluent APIs to deal with web elements.

我们也可以用WebElementFacade来取代WebElement,它提供了更流畅的API来处理Web元素。

As Serenity will automatically instantiate any PageObject fields in the JUnit test, the previous test can be rewritten into a much cleaner one:

由于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)");
    }
}

Now we can search using other keywords and match the related search result without making any changes to the GoogleSearchPageObject.

现在我们可以使用其他关键词进行搜索,并匹配相关的搜索结果,而无需对GoogleSearchPageObject做任何修改。

4.2. Async Support

4.2.异步支持

Nowadays, many web pages are served or rendered dynamically. To deal with such cases, PageObject also supports many rich features that enable us to inspect statuses of elements. We can check if the elements are visible, or wait until they are visible before proceeding.

如今,许多网页是动态提供或渲染的。为了应对这种情况,PageObject也支持许多丰富的功能,使我们能够检查元素的状态。我们可以检查元素是否可见,或者等到元素可见后再进行操作。

Let’s enhance the resultMatches method by ensuring that the element we want to see is visible:

让我们加强resultMatches方法,确保我们想看到的元素是可见的。

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

If we don’t expect to wait for too long, we can explicitly specify the timeout on waiting actions:

如果我们不期望等待太长时间,我们可以明确指定等待动作的超时。

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

5. Screenplay Pattern

5.剧本模式

The Screenplay Pattern applies SOLID design principles to automated acceptance testing. A general understanding of Screenplay Pattern can be explained in the given_when_then context as:

Screenplay模式将SOLID设计原则应用于自动化验收测试。对Screenplay模式的一般理解可以在given_when_then上下文中解释为。

  • given – an Actor that is capable of performing some Task
  • when – the Actor performs the Task
  • then – the Actor should see the effect and verify the results

Now let’s fit our previous test scenario into Screenplay Pattern: given a user Kitty that can use Google, when she searches ‘baeldung’ on Google, then Kitty should see Eugen’s name in the results.

现在让我们把之前的测试场景融入到Screenplay模式中:给定一个可以使用谷歌的用户Kitty,当她在谷歌上搜索 “baeldung “时,Kitty应该在结果中看到Eugen的名字。

First, define tasks that Kitty can perform.

首先,定义Kitty可以执行的任务。

  1. Kitty can use 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. Kitty can make a search on Google:
    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. Kitty can see Google search results:
    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();
        }
    }

Also, we have already defined the Google search PageObject:

另外,我们已经定义了谷歌搜索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");
}

Now our main test class would look like:

现在我们的主测试类将看起来像。

@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)"))));
    }
}

After running this test, we shall see screenshots of each step Kitty performed in the test report:

运行该测试后,我们将在测试报告中看到Kitty执行的每个步骤的屏幕截图。

kitty search baeldung

6. Summary

6.总结

In this article, we have introduced how to use Screenplay Pattern with Serenity BDD. Also, with the help of PageObject, we don’t have to interact with WebDrivers directly, making our tests easier to read, maintain and extend.

在这篇文章中,我们介绍了如何用Serenity BDD使用Screenplay模式。同时,在PageObject的帮助下,我们不必直接与WebDrivers交互,使我们的测试更容易阅读、维护和扩展。

For more details on PageObject and Screenplay Pattern in Serenity BDD, check out in the related section of the Serenity documentation.

关于Serenity BDD中PageObject和Screenplay Pattern的更多细节,请查看Serenity文档的相关部分。

As always, the full example code can be found over on the Github.

一如既往,完整的示例代码可以在Github上找到。