kloia Blog

Effective Use of Hooks in Cucumber

Written by Acelya Gul | Dec 12, 2023 12:26:11 PM

Test automation comes with some prerequisites and there are follow-up actions that need to be taken after testing. Cucumber, one of the popular tools for Behavior-Driven Development (BDD), enhances these processes more effectively. Specifically, the hooks feature in Cucumber automates pre and post actions. With the hooks feature, the test automation process becomes more repeatable, consistent, and efficient.

Hooks in Cucumber are code blocks triggered automatically during the execution of a specific test scenario. These code blocks are executed either just before the scenarios start or immediately after they are completed. In testing terminology, such conditions are commonly referred to as test setup and test teardown. In Cucumber, these conditions are referred as 'before hooks' and 'after hooks' identified with @Before and @After tags. 'Before hooks' are use for preparation tasks such as initiating the webdriver, using specific cookie values, while 'After hooks' are usedto automate tasks after the test such as saving screenshots, performing clean-up, or generating reports. Using hooks ensures a smooth flow throughout the test process, leading to a more systematic and organized execution from start to finish.

 

What are the advantages of using Hooks?

Given the complex nature of test automation, we need tools that make processes simple and manageable. The hook feature in Cucumber serves this very purpose. So, what are the advantages of using hooks? Here are some key benefits:

Modularity and Reuse:

With hooks, steps that are common to test processes, such as specific start and end steps, are centralized and can be easily reused in different test cases. This encourages a modular structure of test cases and code reusability.

Consistency and Efficiency:

Hooks ensure that scenarios start or end on a standardized basis and provide a base environment for tests.

Resource Optimization:

With hooks, it may be possible to use system or application-side resources more efficiently. Because hooks help with reuse at every level, they help prevent unnecessary and frequent use of resources.

What are the different types of Cucumber Hooks?

Hooks are defined as blocks of code that are automatically triggered at the start and end of test cases and play a critical role in improving the efficiency and streamlining of test processes. There are four main hook categories defined in Cucumber. These are:

  • Scenario Hooks,
  • Step Hooks,
  • Conditional Hooks, and 
  • Global Hooks. 

These four different types of hooks allow test processes to be managed more effectively, while at the same time enabling tests to be performed in a more consistent and controlled manner. Especially in complex test scenarios, the structural advantages provided by hooks contribute to the execution of test processes with fewer errors and higher efficiency.

 

Scenario Hooks:

Scenario hooks are designed to be executed for each scenario within a test suite. There are two main types of Scenario hooks: Before and After. These hooks are used to set preconditions before a scenario starts and to perform clean-up activities after a scenario has finished.

Before:

This hook runs just before each scenario starts. It is used to set prerequisites such as preparing the test environment, initializing required data structures, or configuring dependencies.

 

 @Before
public void doSomethingBefore() {
    // Do something before each scenario
}

After:

This hook, which runs after each scenario is completed, is used to perform the necessary cleaning operations afterward. For example, closing opened resources, deleting temporary data, or saving test results can be done with this hook.


@After public void doSomethingAfter(Scenario scenario) { // Do something after the scenario }

Step Hooks:

Step hooks are activated before and after each step of the test cases. These hooks work on the principle of 'invoke around', which means that if a BeforeStep hook is triggered, the AfterStep hook will be triggered regardless of the result of the associated step. This feature allows special operations to be performed at the beginning and end of each test step, providing detailed control and customization at each stage of the test process.

BeforeStep:


@BeforeStep public void doSomethingBeforeStep(Scenario scenario){ }

AfterStep:

 @AfterStep
public void doSomethingAfterStep(Scenario scenario){
}

Conditional Hooks:

Conditional hooks are selected based on the labels of the scenarios and run only under certain conditions. These hooks are not generic to every scenario as they are specific to certain tags and can be defined as @Before(“tagName”) or @After(“tagName”).


@After("@loginRequired and not @guestUser") public void tearDownLogin(Scenario scenario){ // This hook only works at the end of scenarios with the 'loginRequired' tag and no 'guestUser' tag; for example, user logouts can be performed here. }

Global Hooks:

Global hooks are special hooks that run at the very beginning and the very end of the test process. These hooks are triggered only once before all scenarios start or after all scenarios are completed, thus managing the global start and end actions of the test process.

BeforeAll

BeforeAll runs before all scenarios start. This hook is used for a broad setup at the beginning of the test process or to set initial conditions.

 @BeforeAll
public static void beforeAll() {
   // Runs before all scenarios
}

AfterAll

AfterAll runs after all scenarios have been completed. This hook is used for general cleanup at the end of the test process or for operations such as releasing resources.


@AfterAll public static void afterAll() { // Runs after all scenarios }

What are Hooks example use cases?

The key to success in test automation is to use the right tools in the right places. Hooks is one of these tools and when applied correctly, it adds value to different stages of automation. Here are some enlightening examples of hooks application scenarios:


Starting and closing the Browser:

Hooks are ideal for steps that need to be performed in common at the beginning and end of tests. For example, if a web application is being tested, hooks can be used to manage the launch of a web browser before the test starts and the shutdown of the browser at the end of the test.

 
 
public class DriverHooksExample {
    WebDriver driver;

    @Before
    public void setup() {
        System.setProperty("webdriver.chrome.driver", "driverPath/chromedriver");
        driver = new ChromeDriver();
    }

    @After
    public void tearDown() {
        driver.quit();
    }
}
 

Database Operations:

Certain database operations may need to be performed during tests. For example, creating a specific database state before testing or undoing changes in the database after testing can be automated with hooks.

 

 public class DatabaseHooks {

    private Connection connection;

    @Before
    public void connectToDatabase() {
        try {
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/kloiaDB", "kloia", "secret");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @After
    public void closeDatabaseConnection() {
        try {
            if (connection != null && !connection.isClosed()) {
                connection.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Automatic Reporting and Screen Capturing:

Sometimes unexpected errors are encountered in test automation processes. In such cases, automatic reporting or taking screenshots at the time of the error can be very useful to analyze the problem later. With hooks, it is possible to configure such operations to be performed automatically at the end of each test case.

 

 public class ReportingHooks {

    WebDriver driver;

    @After
    public void captureScreenshotOnFailure(Scenario scenario) {
        if (scenario.isFailed()) {
            try {
                TakesScreenshot ts = (TakesScreenshot) driver;
                File source = ts.getScreenshotAs(OutputType.FILE);
                FileUtils.copyFile(source, new File("./KloiaTestScreenShots/" + scenario.getName() + ".png"));
                System.out.println(“Error Detected, Screenshot taken ");
                }
                catch (Exception e) {
                    System.out.println("Exception while taking screenshot: " + e.getMessage());
                }
            }
        }
    }

Hooks' Efficient Utilization Strategies

Some features are available to use Cucumber hooks more efficiently. Below are some examples:

Using Customized Tags:

When using hooks, it is possible to determine which tests will be run with which hooks by creating customized tags. This feature allows hooks to run only on certain tests and prevents unnecessary processing.


public class Hooks { @Before("@Smoke") public void test1() { System.out.println("It will only start before @Smoke.”); } @After("@Smoke") public void test2() { System.out.println("It will only start after @Smoke."); } }

Avoiding Complex Operations:

Performing complex and time-consuming operations on hooks can adversely affect the overall performance of the tests. Therefore, care should be taken to perform only necessary and fast operations on hooks.

 @Before
public void complexSetup() {
    application.LengthySetupMethod();
    driver = new ChromeDriver();
}

Logging for Debugging:

In test automation, there are usually many test cases, and each scenario may fail at different stages and for different reasons. Therefore, it is very important to keep a log record in order to quickly identify failed steps and understand what went wrong with these steps. Hooks are an excellent way to keep detailed logs with the right level of granularity:


public class LoggingHooks { private static final Logger logger = Logger.getLogger(LoggingHooks.class.getName()); WebDriver driver; @Before public void setup() { logger.info("Test is starting.."); driver = new ChromeDriver(); logger.info("Browser successfully started."); } }
 

Taking Actions According to Scenario Situations:

In test automation, certain actions may need to be taken depending on the results of scenarios. Especially in integrated test processes, the failure of one test may affect others or cause some tests to be skipped. With hooks, different actions can be performed automatically according to the result of the scenario.

 

 public class
ScenarioOutcomeHooks {
    WebDriver driver;
    @After
    public void handleScenarioOutcome(Scenario scenario) {
        if (scenario.isFailed()) {
            System.out.println("The scenario failed: " + scenario.getName());
            // You can also add extra information or screenshots here if you wish.
        } else if (scenario.isSkipped()) {
            System.out.println("The scenario skipped: " + scenario.getName());
        } else {
            System.out.println("The scenario was successful: " + scenario.getName());
        }
    }
}


Aspects to be Considered in the Use of Hooks

1. Avoid Overuse

Harnessing the power of hooks is important, but overuse should be avoided. Unnecessarily defining too many hooks for each scenario can complicate your scenarios and make them difficult to maintain. Avoid repetitive code when using hooks, and define common operations within a function to avoid code repetition. It is important to choose the location of each hook well.

2. Avoid Complex Operations

Hooks should generally be used for low-level and repetitive transactions. Putting complex transactions into hooks can make it difficult to understand and maintain your scenarios. Additional complexity also hurts test execution performance.

3. Proper Naming

Naming hooks with descriptive names makes it easier to understand when or why the script works.

4. Managing Dependencies Correctly

When using hooks, be careful to manage the dependencies between scenarios well. Take care to maintain the principle of independence so that one scenario does not affect others.

5. Adaptability and Flexibility

Use hooks to meet the needs of your scenarios and create simple structures that you can change as needed.

Conclusion

Hooks help us optimize testing processes catch errors earlier. This leads to higher quality and  successful projects. The level of automation and coordination that goes into testing has a direct impact on the quality and delivery time of the application. Using hooks to improve the success of software projects makes testing processes more effective and efficient. Therefore, knowing how to use hooks effectively is a critical part of a test engineer's skill set.