The main purpose of this project is to present how to write end-2-end acceptance tests for web applications.
@Test code doesn't interact with tested website directly.
Instead it makes use of 'framework' which purpose is to model tested application in the test code.
This approache makes tests less fragile to changes in the UI, increase their readability and lower cost of maintenance.
Readable, self-explanatory tests serves also as a great long-lived documentation of the project.
Framework also hides away technical details of the sleneium framework which is used to interact with the browser. It also enables to write tests in more declarative way, focuses on the application behaviour rather then current architecture.
Locking behaviour instead of implementation in the tests make them less likely to break because of the change.
Here is presentation outlining some of the concepts the project.
the only constant is change. -Heraclitus, a Greek philosopher
Framework consists of three layers which absorb impact of changes of the tested application.
Layers
Contains low level details of selenium framework such as finding elements on the web page, perform actions on them etc. WebElements are located by CSS selectors. The likelihood of change in this layer is very high. For instance, some functionality has been moved from one place to another in the page. Hence, selectors should be as precise as possible.
//#### login ###
@FindBy(css = ".chiudi")
private WebElement loginPopUp;
@FindBy(css = ".dx > form > input:nth-child(1)")
private WebElement loginEmail;
@FindBy(css = ".dx > form > input:nth-child(2)")
private WebElement loginPassword;
@FindBy(css = ".dx > form > input.login")
private WebElement loginSubmitButton;
loginPopUp.click();
loginPassword.sendKeys(password);
loginPassword.sendKeys(password);
Page object is a class, which structure reflects structure of the HTML page again which you want to write a test. It provides coarse-grained, application-specific API and hides technical details from the lower layer.
HomePage open()
HomePage gotoLoginPopUp()
HomePage fillPassword(String password)
RegistrationPage gotoRegistrationPage()
CompanySearchPage submitCompanySearchForCompanySearchPage()
...
Page object methods reflects also the flow of the application. Invocation of a given functionality can result in different flow which is reflected by the methods returned value (page).
public HomePage clickLogInOnSuccess() {
loginSubmitButton.click();
return new HomePage(driver);
}
public LoginPage clickLogInOnFailure() {
loginSubmitButton.click();
return new LoginPage(driver);
}
Page object API enables method chaining, providing fluent Interface.
homePage
.gotoLoginPopUp()
.fillEmail(userCredentials.email())
.fillPassword(userCredentials.password())
.clickLogInOnSuccess();
Action Object is a function which encapsulates frequently performed action in the system such as user login etc. Algorithm of a given action there is only in one place (DRY: Don't repeat yourself).
@ActionObject
public class UserLogInAction implements BaseAction<HomePage, HomePage> {
@Override
public HomePage execute(HomePage homePage) {
return homePage
.gotoLoginPopUp()
.fillEmail(userCredentials.email())
.fillPassword(userCredentials.password())
.clickLogInOnSuccess();
}
Assert object is a wrapper for object against which we want perform same verifications (assertions). They main purpose is to hide low level assertions behind domain-specific API, hence increasing readability of the tests. Assert object are write for page objects and data returned from back-office.
//then
thenBackOfficeUser(backOfficeUser).isEqualTo(user).isRegisteredAsCompany().hasNotConfirmedStatus();
private BackOfficeUserAssert thenBackOfficeUser(BackOfficeUser backOfficeUser) {
return new BackOfficeUserAssert(backOfficeUser);
}
public BackOfficeUserAssert isEqualTo(User userData) {
assertThat(userData.email()).isEqualTo(backOfficeUser.email());
assertThat(userData.firstName()).isEqualTo(backOfficeUser.firstName());
assertThat(userData.lastName()).isEqualTo(backOfficeUser.lastName());
return this;
}
Tests make use of:
- page objects
- assert objects
- action objects
It helps to separates code of the test from low level, technical details (selenium). Tests are written in declarative, kind of BDD way. They lock behavior instead of sequence of steps to perform. Because of this approach they are less susceptible to change and provide more readable form.
@Test
public void successfulLoggedIn() {
//given
HomePage homePage = new HomePage(driver).open();
//when
homePage = homePage.execute(new UserLogInAction().withCredentials(user));
//then
thenHomePage(homePage).hasLoggedInUserAs(user);
}
ExtentReport is used in order to create report from tests execution.
In order to turn report generation on add folowing lines to your test suite.
@BeforeClass
public static void setUp() {
ReportLogging.createReport();
}
@AfterClass
public static void tearDown() {
ReportLogging.closeReport();
}
By default report is created in the target folder. Check reportsPath property to find out more.
Example of a sample report here.
In order to run tests locally on your machine clone this repo.
Default configuration there is in configs/config_default.yml.
In order to override default values create new file configs/config.yml by copping file configs/config_default.yml with changed properties (custom file). Alternativelly you can provide new values as a system properties.
Some tests (logging, user creation) requires credentials. Path to the credential file is set via property credentialsPath. Unfortunatelly files with credentials are not commited to the public repo. Because of it only one type of test can be performed by the anonymous user - company search.
In order to run the example tests run test suite ExampleTestSuite.class.
Currently three features are tested:
- company search
- user registration via internal form
- user logging (registered previously by internal form)
Most of the resources is in Polish language.
- Sławomir Sobótka -- Czego mama nigdy nie mówiła Ci na temat testowania automatycznego
- Testy automatyczne Selenium - sposób na obronę przed smokami
- Confitura 2013 - Michał Rokicki - Testy funkcjonalne (Selenium) w praktyce
- PageObject - Martin Fowler
- Page Object Design Pattern
- Czego mama nigdy nie mówiła Ci na temat testowania automatycznego by Sławek Sobótka
In case of any questions don't hesitate to contact me: