How to use thirtyfour crate with Cucumber-rs: Part 1

This post shows how to set a webdriver instance produced by thirtyfour as a member of the World struct defined by Cucumber-rs. We hope to follow up with one more simple post on How to use thirtyfour crate with Cucumber-rs showing ways to write browser driven BDD test automation in Rust using thirtyfour and Cucumber-rs.


Why bother writing browser automation in Rust?

Qxf2 has begun to enjoy writing Rust. All engineers at our company have done the hard work of getting familiar with Rust and trying small projects. We are now in the phase of preparing to write tests in Rust ecosystems. We are looking to work with companies that use Rust. UI automation is (thankfully) not needed as much in the Rust ecosystem. However, we do foresee it being a tiny portion of our testing strategy. In the Rust world, thirtyfour is a popular crate that works with Selenium webdriver. Cucumber-rs is a framework that lets us write BDD tests.


Setup

The setup for running Cucumber Rust tests with thirtyfour crate requires you to add the following crate to the Cargo.toml file

cucumber = "0.19"
thirtyfour = { version = "0.31.0", features = ["component"] }

Remember to add the features along with the version in thirtyfour crate. We faced an issue here while not using it. The component feature flag enables the component to derive macro via thirtyfour macros.


Problem statement

Things were going well while writing the Cucumber test with Rust when we encountered an issue where we wanted the Webdriver to be global so that it can be used across all the functions in the code.

I had the following struct in the code.

#[derive(cucumber::World, Default)]
struct World {
    pub driver: Option<WebDriver>,
}

I have tried to use the driver in the World struct, so that it can be used across the code. Straight way right but Wait!, the problem comes here. If you only have the following struct and use World::run in your main function to run the tests, it will complain of the following error.

`World` doesn’t implement `std::fmt::Debug` and the trait `std::fmt::Debug` is not implemented for `World`

So, the obvious thing is to add #[derive(Debug)] in the World Struct and make it work but adding that in derive throws the following:

`thirtyfour::WebDriver` doesn’t implement `std::fmt::Debug`

So, it’s obvious from the message now that thirty-four::WebDriver doesn’t implement Debug trait, and as we are working with web automation, we would need WebDriver. So, what’s next?


Implementing Debug trait

So, the solution for the above problem was manually implementing the debug trait for WebDriver. To implement the trait, we use a function fmt which has a self-reference to the struct World and a mutable instance of Formatter. Since Rust is not able to automatically derive the debug trait for the struct element “driver”, we assist it as follows.

impl fmt::Debug for World {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("World")
            .field("driver", &self.driver.as_ref().map(|_| "<WebDriver>"))
            .finish()
    }
}

What is happening in the above snippet? We explicitly tell Rust that we are providing the Debug implementation for the “driver” field of the World struct. Since we do not really need the details of the Webdriver element in the debug statement, we just map the field to a string literal. “WebDriver” in our case. You can use any other string literal here or even implement a function to get the driver’s version and browser’s name. The rest of the magic in this trait implementation is the standard way to implement the Debug trait for an element.


Initializing the driver to be used across the functions

In the beginning, I mentioned that we wanted to use the driver across all the functions and hence we implemented it in World struct.

To use the driver across, we first need to initialize it.

fn example_initialize(world: &mut World) -> WebDriverResult<()> {
//code
world.driver=Some(driver); //Initialize the driver
}

Once, you have initialized the driver, next we can use it as a reference in other functions.

fn example_use_driver(world: &mut World) -> WebDriverResult<()> {
let driver = world.driver.as_ref().expect("The driver is needed before you proceed");
//code
}

Running the tests

Here is an example of a BDD test with a scenario outline running on my machine. I hope to be able to share the entire code for it in a future tutorial.


Hire Qxf2 for all your testing needs

Qxf2 is the home of the technical tester. We have experience with implementing a wide variety of testing stacks. Our engineers work well with small engineering teams and love the challenge of testing early stage products. If you are looking for good software testers who can work on Rust or other projects, drop us a note here.


Leave a Reply

Your email address will not be published. Required fields are marked *