1. Introduction
1.绪论
Cucumber hooks can come in handy when we want to perform specific actions for every scenario or step, but without having these actions explicitly in the Gherkin code.
Cucumber钩子在我们希望为每个场景或步骤执行特定的操作,但又不在Gherkin代码中明确规定这些操作时可以派上用场。
In this tutorial, we’ll look at the @Before, @BeforeStep, @AfterStep, and @After Cucumber hooks.
在本教程中,我们将了解@Before、@BeforeStep、@AfterStep和@After Cucumber挂钩。
2. Overview of Hooks in Cucumber
2.Cucumber中的Hooks概述
2.1. When Should Hooks Be Used?
2.1.什么时候应该使用钩子?
Hooks can be used to perform background tasks that are not part of business functionality. Such tasks could be:
钩子可以用来执行不属于业务功能的后台任务。这样的任务可以是。
- Starting up a browser
- Setting or clearing cookies
- Connecting to a database
- Checking the state of the system
- Monitoring
A use case for monitoring would be to update a dashboard with the test progress in real-time.
监测的一个用例是实时更新测试进度的仪表板。
Hooks are not visible in the Gherkin code. Therefore, we should not see them as a replacement for a Cucumber Background or a given step.
钩子在Gherkin代码中是不可见的。因此,我们不应该把它们看作是Cucumber Background或特定步骤的替代物。
We’ll look at an example where we use hooks to take screenshots during test execution.
我们将看一个例子,在测试执行过程中,我们使用钩子来进行截图。
2.2. Scope of Hooks
2.2.钩子的范围
Hooks affect every scenario. Therefore, it’s good practice to define all hooks in a dedicated configuration class.
钩子会影响每个场景。因此,在一个专门的配置类中定义所有的钩子是很好的做法。
It’s not necessary to define the same hooks in every glue code class. If we define hooks in the same class with our glue code, we’d have less readable code.
没有必要在每个胶水代码类中定义相同的钩子。如果我们把钩子定义在与胶水代码相同的类中,我们的代码可读性就会降低。
3. Hooks
3.钩子
Let’s first look at the individual hooks. We’ll then look at a full example where we’ll see how hooks execute when combined.
让我们先看一下各个钩子的情况。然后我们将看一个完整的例子,在那里我们将看到钩子在组合时如何执行。
3.1. @Before
3.1.@Before
Methods annotated with @Before will execute before every scenario. In our example, we’ll start up the browser before every scenario:
带有@Before注释的方法将在每个场景之前执行。在我们的例子中,我们将在每个场景之前启动浏览器。
@Before
public void initialization() {
startBrowser();
}
If we annotate several methods with @Before, we can explicitly define the order in which the steps are executed:
如果我们用@Before来注释几个方法,我们可以明确地定义执行步骤的顺序:。
@Before(order=2)
public void beforeScenario() {
takeScreenshot();
}
The above method executes second, as we pass 2 as a value for the order parameter to the annotation. We can also pass 1 as a value for the order parameter of our initialization method:
上面的方法是第二次执行,因为我们把2作为order参数的值传递给注解。我们也可以将1作为我们初始化方法的order参数值。
@Before(order=1)
public void initialization()
So, when we execute a scenario, initialization() executes first, and beforeScenario() executes second.
因此,当我们执行一个场景时,initialization()首先执行,而beforeScenario()第二执行。
3.2. @BeforeStep
3.2. @BeforeStep
Methods annotated with @BeforeStep execute before every step. Let’s use the annotation to take a screenshot before every step:
用@BeforeStep注解的方法在每一步之前执行。让我们使用注解在每一步之前进行截图。
@BeforeStep
public void beforeStep() {
takeScreenshot();
}
3.3. @AfterStep
3.3.@AfterStep
Methods annotated with @AfterStep execute after every step:
用@AfterStep注解的方法在每个步骤后执行。
@AfterStep
public void afterStep() {
takeScreenshot();
}
We’ve used @AfterStep here to take a screenshot after every step. This happens regardless of whether the step finishes successfully or fails.
我们在这里使用了@AfterStep来在每个步骤之后进行截图。这发生在无论该步骤是成功完成还是失败。
3.4. @After
3.4.@After
Methods annotated with @After execute after every scenario:
用@After注解的方法在每个场景后执行。
@After
public void afterScenario() {
takeScreenshot();
closeBrowser();
}
In our example, we’ll take a final screenshot and close the browser. This happens regardless of whether the scenario finishes successfully.
在我们的例子中,我们会拍一张最后的截图,然后关闭浏览器。这发生在无论场景是否成功完成。
3.5. The Scenario Parameter
3.5.Scenario参数
The methods annotated with a hook annotation can accept a parameter of type Scenario:
带有钩子注解的方法可以接受一个Scenario类型的参数。
@After
public void beforeScenario(Scenario scenario) {
// some code
}
The object of type Scenario contains information on the current scenario. Included are the scenario name, number of steps, names of steps, and status (pass or fail). This can be useful if we want to perform different actions for passed and failed tests.
Scenario类型的对象包含关于当前情景的信息。包括场景名称、步骤数量、步骤名称和状态(通过或失败)。如果我们想对通过和失败的测试执行不同的操作,这可能很有用。
4. Hook Execution
4.钩子的执行
4.1. Happy Flow
4.1.快乐之流
Let’s now look at what happens when we run a Cucumber scenario with all four types of hooks:
现在让我们看看,当我们用所有四种类型的钩子运行Cucumber场景时会发生什么。
Feature: Book Store With Hooks
Background: The Book Store
Given The following books are available in the store
| The Devil in the White City | Erik Larson |
| The Lion, the Witch and the Wardrobe | C.S. Lewis |
| In the Garden of Beasts | Erik Larson |
Scenario: 1 - Find books by author
When I ask for a book by the author Erik Larson
Then The salesperson says that there are 2 books
Scenario: 2 - Find books by author, but isn't there
When I ask for a book by the author Marcel Proust
Then The salesperson says that there are 0 books
Looking at the result of a test run in the IntelliJ IDE, we can see the execution order:
看一下IntelliJ IDE中测试运行的结果,我们可以看到执行的顺序。
First, our two @Before hooks execute. Then before and after every step, the @BeforeStep and @AfterStep hooks run, respectively. Finally, the @After hook runs. All hooks execute for both scenarios.
首先,我们的两个@Before钩子执行。然后在每一步之前和之后,@BeforeStep和@AfterStep钩子分别运行。最后,@After钩子运行。所有钩子在两种情况下都会执行。
4.2. Unhappy Flow: a Step Fails
4.2.不快乐的流程:一个步骤失败了
Let’s see what happens if a step fails. As we can see in the screenshot below, both the @Before and @After hooks of the failing step are executed. The subsequent steps are skipped, and finally, the @After hook executes:
让我们看看如果一个步骤失败会发生什么。正如我们在下面的截图中看到的,失败的步骤的@Before和@After钩子都被执行。随后的步骤被跳过,最后,@After钩子被执行。
The behavior of @After is similar to the finally-clause after a try-catch in Java. We could use it to perform clean-up tasks if a step failed. In our example, we still take a screenshot even if the scenario fails.
@After的行为类似于Java中try-catch之后的finally-lause。如果一个步骤失败,我们可以用它来执行清理任务。在我们的例子中,即使方案失败了,我们仍然会进行截图。
4.3. Unhappy Flow: a Hook Fails
4.3.不快乐的流程:挂钩失败
Let’s look at what happens when a hook itself fails. In the example below, the first @BeforeStep fails.
让我们来看看当一个钩子本身失败时会发生什么。在下面的例子中,第一个@BeforeStep失败。
In this case, the actual step doesn’t run, but it’s @AfterStep hook does. Subsequent steps won’t run either, whereas the @After hook is executed at the end:
在这种情况下,实际的步骤不会运行,但它的@AfterStep钩子会运行。后续的步骤也不会运行,而@After钩子会在最后执行。
5. Conditional Execution with Tags
5.带标签的条件执行
Hooks are defined globally and affect all scenarios and steps. However, with the help of Cucumber tags, we can define exactly which scenarios a hook should be executed for:
钩子是全局定义的,会影响所有场景和步骤。然而,在Cucumber标签的帮助下,我们可以准确地定义一个钩子应该在哪些场景下执行。
@Before(order=2, value="@Screenshots")
public void beforeScenario() {
takeScreenshot();
}
This hook will be executed only for scenarios that are tagged with @Screenshots:
这个钩子只对被标记为@Screenshots的场景执行。
@Screenshots
Scenario: 1 - Find books by author
When I ask for a book by the author Erik Larson
Then The salesperson says that there are 2 books
6. Java 8
6.JAVA 8
We can add Cucumber Java 8 Support to define all hooks with lambda expressions.
我们可以添加Cucumber Java 8 Support,用lambda表达式定义所有钩子。
Recall our initialization hook from the example above:
回顾我们在上面的例子中的初始化钩子。
@Before(order=2)
public void initialization() {
startBrowser();
}
Rewritten with a lambda expression, we get:
用lambda表达式重写,我们得到。
public BookStoreWithHooksRunSteps() {
Before(2, () -> startBrowser());
}
The same also works for @BeforeStep, @After, and @AfterStep.
这也适用于@BeforeStep、@After,和@AfterStep。
7. Conclusion
7.结语
In this article, we looked at how to define Cucumber hooks.
在这篇文章中,我们研究了如何定义Cucumber钩子。
We discussed in which cases we should use them and when we should not. Then, we saw in which order hooks execute and how we can achieve conditional execution.
我们讨论了在哪些情况下我们应该使用它们,哪些情况下我们不应该。然后,我们看到了钩子的执行顺序以及我们如何实现条件执行。
Finally, we saw how we could define hooks with Java 8 lambda notation.
最后,我们看到了如何用Java 8的lambda符号来定义钩子。
As usual, the complete source code of this article is available over on GitHub.
像往常一样,本文的完整源代码可在GitHub上获得,。