Tool | Intention | Version | Docs |
---|---|---|---|
Java | Programming Language | JDK - 20 | JDK20 |
Maven | Build Tool and Dependency Management | 3.9.5 | Maven 3 |
Selenium | UI Test Automation | 4.15.0 | Selenium 4 |
JUnit | Test Framework | 4.13.2 | JUnit 4 |
Cucumber | BDD Framework | 7.15.0 | Cucumber 7 |
-
Create a new project with:
- Java as programming language 🌐
- Maven as build tool 🛠
- Git for source version control 🔄
-
Add necessary dependencies 📦
- Selenium, JUnit 4, Cucumber - Java, Cucumber - JUnit
-
Create folder structure 📁
- Pages, Step_definitions, Utilities, Runner, Resources.features
-
test ├── java │ ├── pages │ ├── runner │ ├── step_definitions │ └── utilities └── resources └── features
-
Start by creating your first Cucumber file 🥒
- feature-name.feature
- make sure the file extension is
.feature
-
Add necessary plugins 🔄
- Cucumber for Java
- Ensure Gherkin is installed
-
Running your first feature: 🏃
- I. Create a StepDefinition class containing Java methods that define each step from the feature file
package step_definitions; public class LoginSteps(){ // Example of a Step Definition in Java @Given("user is on the login page") public void userIsOnLoginPage() { // code to navigate to login page } }
- II. Create a TestRunner class connecting feature file steps with Java methods from step definitions
- TestRunner uses JUnit 4 annotation @RunWith() to run tests with Cucumber.class type
- Customize binding and framework execution with @CucumberOptions():
feature = "path/to/features"
: provides the path to features of the frameworkglue = "step definitions location"
: connects the step definitions with feature files stepsdryRun = true/false
options:- If set to true, it checks if all steps in the feature file are defined but doesn't run test cases
- If set to false, it runs the test cases and throws an error if steps aren't defined
monochrome = true
: makes the console logs more readableplugin = {"pretty", "html:target/reports/cucumber-reports.html"}
tags = "@smoke"
@RunWith(Cucumber.class) @CucumberOptions( features = "src/test/resources/features", glue = "step_definitions", monochrome = true, dryRun = false, tags = ("@runValid and @smoke"), plugin = {"pretty", "html:target/reports/cucumber-reports.html"} ) public class TestRunner { // no need to add code here }
- Customize binding and framework execution with @CucumberOptions():
- TestRunner uses JUnit 4 annotation @RunWith() to run tests with Cucumber.class type
- I. Create a StepDefinition class containing Java methods that define each step from the feature file
-
Cucumber Keywords definitions in feature files 🗝️
Feature
: Short description of the feature to be tested- Examples:
#Example 1: Feature: User Login Functionality #Example 2: Feature: Reset Password Functionality
- Examples:
Background
Will run the steps of background before each Scenario- Examples:
#Example 1: Background: Preliminary Steps Given user navigates to hrm login page
- Examples:
Scenario
: Brief use case description from a user perspective- Examples:
#Example 1: Scenario: Login with valid credentials #Example 2: Scenario: Login with invalid credentials
- Examples:
- Given, When, Then, And, But, * Keywords:
Given
- Preconditions or starting points- Examples:
#Example 1: Given user navigates to login page #Example 2: Given admin user lands on the home page
- Examples:
When
- Main actions performed- Examples:
#Example 1: When user enters username and password #Example 2: When user clicks on admin tab
- Examples:
Then
- Outcome or result of actions, usually at this step you need to perform an assertion- Examples:
#Example 1: Then user is redirected to the homepage #Example 2: Then user can see an error message
- Examples:
And
- Connects multiple steps within Given, When, or Then- Used to add more detail or steps without repeating the Given, When, or Then keywords
- Examples:
#Example: notice how And keyword connects any steps on Given, When, Then Scenario: Login with valid credentials Given user is on the login page And user has a valid account When user enters their username And user enters their password And user clicks on the login button Then user is redirected to the dashboard And user receives a welcome message
But
- Describes an exception or a contrasting scenario- Example:
#Example: notice how But keyword provides a logical output when the credentials # are about to expire you still can login but a message will be shown Scenario: Login with expiring credentials Given user is on the login page And user has a valid account When user enters their username And user enters their password And user clicks on the login button Then user is redirected to the dashboard But user can see a warning message to change credentials
- Example:
*
- Asterisks:- The asterisk * is a more generic step keyword that can be used in place of any of the other keywords ( Given, When, Then, And, But).
- It is often used when the specific type of step (setup, action, or assertion) is either clear from context or not important to the clarity of the scenario.
- The use of * doesn't imply a technical or undefined behavior; it's more about flexibility in writing
the scenario.
# Example using steps with keywords: Given I am logged in And I have items in my cart When I go to the checkout page Then I should see the total price # Example using steps with asteriks: * I am logged in * I have items in my cart * I go to the checkout page * I should see the total price
-
In the IT world, the term "framework" is used extensively and refers to a set of pre-written code, tools, and guidelines that aid in software development. To put it in a simpler context:
### Analogy: Furnishing with an IKEA Bookshelf
Imagine you want to furnish your home with a new bookshelf. You have two main options:
- DIY Approach: Design and build the bookshelf from scratch. This involves selecting the wood, cutting it to size, sanding, joining, and painting. It requires significant skill, time, and effort.
- IKEA Approach: Buy a bookshelf from IKEA, which comes as a flat-pack with all the pieces pre-cut, holes pre-drilled, and includes the necessary tools and instructions for assembly.
### Correlation with IT Frameworks
-
Pre-Made Components:
- IKEA: Comes with pre-cut pieces.
- IT Framework: Provides pre-written code and components, saving time and effort.
-
Instructions and Guidelines:
- IKEA: Assembly instructions guide the process.
- IT Framework: Offers guidelines and conventions for structuring code and building applications.
-
Customization:
- IKEA: Allows some customization (e.g., placement, additional decorations).
- IT Framework: While using pre-built components, customization in functionality and design is possible.
-
Consistency and Quality:
- IKEA: Known for consistent quality and design.
- IT Framework: Ensures a level of consistency and reliability, incorporating best coding practices.
-
Community and Support:
- IKEA: Online forums and videos for assistance.
- IT Framework: Supported by large communities, offering additional tools and resources.
- Participants: Product Owner, Development Team, Scrum Master (in Scrum).
- Purpose: To plan the work for the upcoming sprint. This is where user stories are discussed, and acceptance criteria are defined to ensure everyone has a clear understanding of what needs to be done.
- Participants: A representative from Business (usually Product Owner), Development, and Testing.
- Purpose: This is a more informal, yet focused discussion where these three perspectives converge to define what a user story means, how it will function, and how it will be tested.
- Participants: Product Owner, Development Team, and often the Scrum Master.
- Purpose: To refine and estimate backlog items, including user stories. Acceptance criteria are refined here to ensure they are clear and testable.
- Approach: Focuses on defining a user story with acceptance tests (AT), ensuring everyone involved clearly understands the requirements. Developers and Testers then work in parallel to fulfill these criteria.
- Approach: BDD extends ATDD by emphasizing user behaviors and expected outcomes. It involves writing acceptance tests in a language that describes the behavior of the software from the end-user's perspective.
User Story: "As a user, I want to reset my password so that I can access my account if I forget it."
Acceptance Criteria: - Users can initiate a password reset from the login page. - A reset link is sent to the user’s registered email, expiring after 24 hours. - Users can set a new password using the reset link.
User Story: "As a user, I want to reset my password to access my account if I forget it."
Acceptance Criteria:
Scenario: Requesting a password reset
Given I am on the login page
When I click on "Forgot Password"
Then I should enter my registered email
Scenario: Receiving a password reset email
Given I requested a password reset
When I submit my email
Then I should receive a reset email within 5 minutes
Scenario: Resetting password using the link
Given I received a reset email
When I use the link to set a new password
Then my password should update and I’m redirected to login
Parameterization in Cucumber is a technique used to create data-driven tests, which allows you to run the same test with different sets of data. It increases the reusability and flexibility of your tests. Here, in given examples we can see the use of different data types like String, Integer, Boolean, and Double.
The idea is that we can pass data directly from the step to methods in step definitions
String parameters
Feature file:
Feature: Login functionality
Scenario: Logging in with different usernames
Given I am on the login page
When I enter "my_username" and "password123"
Then I should be logged in
Step definition:
@When("I enter {string} and {string}")
public void i_enter_username_and_password(String username, String password) {
// Code to enter username and password
}
Integer parameters
Feature file:
Feature: Product quantity selection
Scenario Outline: Selecting different product quantities
Given I have selected a product
When I choose a quantity of 25
Then the total should be updated accordingly
Step definition:
@When("I choose a quantity of {int}")
public void i_choose_a_quantity_of(int quantity) {
// Code to select the quantity
}
Double parameters
Feature file:
Feature: Payment processing
Scenario Outline: Processing payments of different amounts
Given I am making a payment
When I enter the amount 55.99
Then the payment should be processed
Step definition:
@When("I enter the amount {double}")
public void i_enter_the_amount(double amount) {
// Code to enter the payment amount
}
Scenario Outline in Cucumber is a key feature for data-driven testing. It allows you to run the same test scenario multiple times with different sets of input values. This is extremely useful in cases where you need to test the same functionality under various conditions.
Syntax:
Scenario Outline: Generic name for scenario
Given some step "<placeholder1>"
When some step "<placeholder2>"
Examples:
|placeholder1|placeholder2|
|data1 |data2 |
|value1 |value2 |
Here we can see that the steps have a placeholder wrapped in <> angle brackets. Angle brackets makes cucumber look for that placeholder in the Examples table column name Once found it will pick data from that column. Make sure that table column name matches with placeholder in steps. Otherwise, it won't pick the data from there.
The Scenario Outlines are good candidate for:
- Scenarios that are similar in steps and only data changes
- Where each scenario is a new user web app usage
- For example one user can use both username and password as invalid creds
- Others can use differen tabs empty etc..
- Here each of these represents a new user launch to the app.
- We use outlines for:
- Login, Forms with error message fields, search bars...
We can also have more than one Example tables per each Scenario Outline/Template See example
That sets of data can be in different forms - List, Map, List, List
Examples:
@listDataTable
Example: Data table as a List
Given this is a data table as a list
| Omar |
| John |
| Merry |
| Jane |
@Given("this is a data table as a list")
public void this_is_a_data_table_as_a_list(List<String> names) {
// code
}
@mapDataTable
Example: Data Table as a Map
Given this is a data table as a map
| first_name | Omar |
| middle_name | Mark |
| last_name | Ramo |
@Given("this is a data table as a map")
public void this_is_a_data_table_as_a_map(Map<String, String> person){
// code
}
@listOfListsDataTable
Example: Data Table as a List of Lists
Given this is a data table as a List of Lists
| 12313 | Omar Mark | Ramo |
| 13684 | Jonathan Pete Moss | Abernathy |
| 13724 | test2 nn | Anotation |
@Given("this is a data table as a List of Lists")
public void this_is_a_data_table_as_a_list_of_lists(List<List<String>> employees){
// code
}
Sometimes we need to pass our table with headers and in such cases use List of Maps where the header will be the key an column values as value to the key.
@listOfMapsDataTable
Example: Data Table as a List of Maps
Given this is a data table as a List of Maps
| id | first_mid_name | last_name |
| 12313 | Omar Mark | Ramo |
| 13684 | Jonathan Pete Moss | Abernathy |
| 13724 | test2 nn | Anotation |
@Given("this is a data table as a List of Maps")
public void this_is_a_data_table_as_a_list_of_lists(List<Map<String,String>> employees){
// code
}
Solution - Singleton Design Pattern
Singleton: There are 3 base rules to create a Singleton Class
- Private Constructor - to avoid instantiation of this class
- Private Static Member(Property) - contains the instance of the Driver
- Static Public Method - will act as a global access point (getter)
Optional: 4. Static Set Up Method - To intialize the browser to a very specific type (setter) 5. Static Clean Up Method - will basically quit the driver
- The first step we create a file with extension of .properties
- This file will contain such things as: urls, credentials, environment configs etc..
- In order to convert/use this information in our code/scripts
- We need to create an object of Properties class
- Once we create the object of properties it will be empty with no information
- To get the information from the file we will use FileInputStream
- We create an object of FileInputStream
- Then we pass to the constructor the path to the file itself (we usually start from source folder src/)
- Once the information if read , it is stored in the FileInputStream object as 0s and 1s
- Then we can load this information to our properties file so now our properties object has the information written to the .properties file