visit
If you are looking for a simple template for building your own test automation framework for a web-app using Java, Selenium / Selenide and Cucumber — you can find one .This is a brief overview of BDD with specifics on implementing an automated test suite using Java, Selenium and Cucumber.
When trying to leverage BDD in a project, people often get bogged down by the steps required to build an automated test suite as if it is a massive undertaking — it isn’t.
Hopefully this article will demystify the process somewhat and provide some useful tips to get you started with your project.BDD is an extension of with the only real differences in the methodologies being the way test cases in BDD are specified.
BDD uses clear, ubiquitous language for test-cases which are derived ‘from the outside-in’ — i.e. directly from the business requirements and desired business outcomes for the behaviour of the application.
The unambiguous language used in these test cases is typically written in the form ofSo you’ve had an idea for a killer app — you’re going to revolutionise the world by having the perfect login screen!
Let’s start by writing out how we want this to behave in Gherkin.(Note: Background steps are a shorthand for making a set of steps that run for every single scenario)
Feature: Login page for web app
Background:Given I browse to the web app and I am not logged in
Scenario: Login page displayedThen I see a "login" pageAnd the login page has a login formAnd there is a logo on the login page
Scenario: I try to login with an invalid userWhen I login with an invalid userThen I see a "Username or password is incorrect" error message
Scenario: I try to login with a valid userWhen I login with a valid userThen I see a "home" page
The real advantage of writing test cases in this form are that they serve two purposes:
@Given("^I browse to the web app and I am not logged in$")public void iBrowseToTheWebAppAndIAmNotLoggedIn(){// TODO}
@When("^I login with an invalid user$")public void iLoginWithAnInvalidUser(){// TODO}
@Then("^I see a \"([^\"]*)\" error message$")public void iSeeAErrorMessage(String errorMessage){// TODO}
...(Note: Many IDEs have plugins which will auto-magically generate these for you from feature files)
Selenium is a browser automation framework, you can leverage a WebDriver and interact with a web-page using a real browser and through selecting elements on the screen using . There are many different implementations of WebDriver — for various different browsers as well as ‘headless’ implementations where a browser is ran without a GUI.
private static WebDriver _webDriver_;
public static synchronized WebDriver getWebDriverInstance()
{
if(_webDriver_ \== null)
{
DriverPathLoader._loadDriverPaths_(null);
_webDriver_ \= new ChromeDriver();
}
return _webDriver_;
}
}
Example of asserting an element is on a page using standard Selenium:
Assert.assertNotNull(webDriver.findElement(By.cssSelector("#username")).getText());
On its own, Selenium is incredibly powerful, but it can have a few drawbacks — e.g. the verbosity of selectors for selecting elements on a page, handling exceptions and how the framework handles timing and timeouts.
Example of asserting an element is on a page using Selenide:
This is where comes in. It’s essentially just a very clean and easy to use wrapper around the Selenium framework.$("#username").shouldBe(visible);
You’ll agree the above is considerably less verbose and much more readable.
private static final String _PAGE\_NAME_ \= "login";
LoginPage(String basePath)
{
super(basePath);
}
**@Override**
public String getPageName()
{
return _PAGE\_NAME_;
}
**@Override**
public void go()
{
URI loginUri = URI._create_(basePath).resolve(getPageName());
_open_(loginUri.toString());
}
public LoginFormElement loginForm()
{
return new LoginFormElement();
}
}
The class above represents our login page — it returns the main element that exists on the page (LoginFormElement) as a separate class which has its own methods and responsibilities which it handles (such as logging in).
public class LoginFormElement extends AbstractElement<LoginFormElement>{private static final String INPUT_USERNAME = "#username";private static final String INPUT_PASSWORD = "#password";private static final String SUBMIT = "#login_submit";
**@Override**
public LoginFormElement isVisible()
{
_$_(_INPUT\_USERNAME_).shouldBe(_visible_);
_$_(_INPUT\_PASSWORD_).shouldBe(_visible_);
_$_(_SUBMIT_).shouldBe(_visible_);
return this;
}
public void login(String username, String password)
{
_$_(_INPUT\_USERNAME_).setValue(username);
_$_(_INPUT\_PASSWORD_).setValue(password);
_$_(_SUBMIT_).click();
}
}
You will see that CSS element selectors have been defined here for username and password input fields, as well as for a login submit button.
When we build the web-app we just need to ensure that we label the fields and the button with these identifiers and if all has gone well — the tests will pass!
We will almost certainly refactor this (for example we will need to figure out how to inject or know valid credentials for our ‘login with a valid user’ scenario) but this gives us a foundation to build upon.
private LoginPage loginPage = pages.loginPage();
private AlertElement alertElement = pages.alertComponent();
private LoginFormElement loginFormElement = loginPage.loginForm();
@Given("^I browse to the web app and I am not logged in$")
public void iBrowseToTheWebAppAndIAmNotLoggedIn()
{
loginPage.go();
}
@And("^the login page has a login form$")
public void theLoginPageHasALoginForm()
{
loginPage.loginForm().isVisible();
}
@When("^I login with an invalid user$")
public void iLoginWithAnInvalidUser()
{
loginFormElement.login("invalid", "invalid");
}
@Then("^I see a \\"(\[^\\"\]\*)\\" error message$")
public void iSeeAErrorMessage(String errorMessage)
{
alertElement.isVisible().hasMessage(errorMessage);
}
@When("^I login with a valid user$")
public void iLoginWithAValidUser()
{
loginFormElement.login("valid", "valid");
}
@And("^there is a logo on the login page$")
public void thereIsALogoOnTheFrontPage()
{
loginPage.logo().isVisible();
}
}
After some further work writing step definitions and writing page objects, we have an automated test suite which will reliably fail for our login page app (because we haven’t built anything to pass the tests yet).
As you build your application and add more functionality, more of the tests pass. To ensure tests pass, you just need to build the appropriate functionality with the appropriate selectors (as defined in your page object models and elements).
As more requirements get added, you add more tests. Simple.
I’d suggest the project and understanding how the framework fits together and trying to run it (and watching it fail).
The example was described using Java, Cucumber, Selenium and Selenide — it is definitely not the only way to write an automated test suite. Every language has its own set of frameworks but the underlying approaches are generally the same.
There is no reason why you can’t use an entirely different language for your automated tests rather than the language the project is written in - so long as it isn’t too exotic (don’t write it in FORTRAN or assembly!), isn’t completely against your project’s and company’s technology strategy and there are well-embedded skills in your team to support the language.
In fact I’d highly recommend this if the team member responsible for the automated test suite has skills that are more developed in different frameworks or languages. Figure out what works best for your project by considering the capabilities of your team.
Thank you for reading! 🙂