The previous tutorial explains to configure Junit in IntelliJ and run the tests as JUnit Tests. This tutorial shows the steps to run the tests through command line. We can ask, why we need to run the tests through command line?? There are many reasons, one of the reason is to achieve CI/CD. To run the tests in pipeline, they need to be run through command line. Another reason is that we don’t need to open the IDE to run the tests. Third reason is that many reports are only generated (Serenity, Cucumber), if the tests run through command line.
Below is a JUnit5 test.
import io.github.bonigarcia.wdm.WebDriverManager;
import org.junit.jupiter.api.*;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class Demo {
WebDriver driver;
@BeforeEach
public void setUp() {
WebDriverManager.chromedriver().setup();
ChromeOptions chromeOptions = new ChromeOptions();
driver = new ChromeDriver(chromeOptions);
driver.manage().window().fullscreen();
}
@Test
public void Junit5Test() {
driver.get("http://automationpractice.com/index.php");
System.out.println("Title of Page :" + driver.getTitle());
System.out.println("Page URL : " + driver.getCurrentUrl());
Assertions.assertEquals("My Store",driver.getTitle());
}
@AfterEach
public void tearDown() {
driver.close();
}
}
Let us see what happens when we try to run the JUnit tests through Command Line. This command is used to run the tests present in Demo class.
mvn clean test -Dtest=Demo
The output generated by the test is shown below
This shows that surefire-plugin is need to be add to the project to run t he tests successfully through command line.
JUnit5 contains the assertions available as in JUnit 4 Assertions as well there are a few additional new asserts too. In this post, let’s discuss each new assertion in JUnit5 works in detail with examples.
1. assertIterableEquals
The assertIterableEquals() asserts that the expected and the actual iterables are deeply equal. In order to be equal, both iterable must return equal elements in the same order and it isn’t required that the two iterables are of the same type in order to be equal.
Example 1 – In this example, the number of elements as well as the sequence of elements is in the same order in both Iterables. It is not mandatory to have Iterables of the same type, so we can see as one of the Iterable is ArrayList whereas another Iteratble is of type LinkedList.
As both Iterables do not have same number of elements, so the Assertion has failed.
Note:- There are no assertions like assertNotIterableEquals() or assertIterableNotEquals().
2. assertLinesMatch
This Assertion asserts that the expected list of Strings matches the actual list of String. This method differs from other assertions that effectively only check String.equals(Object), in that it uses the following staged matching algorithm: For each pair of expected and actual lines do a) check if expected.equals(actual) – if yes, continue with next pair b) otherwise treat expected as a regular expression and check via String.matches(String) – if yes, continue with the next pair c) otherwise check if an expected line is a fast-forward marker, if yes apply to fast-forward actual lines accordingly (see below) and goto 1.
Example 1 – In the below example, expected has a regular expression that matches with the elements of actual.
When we want to assert that execution of the supplied executable completes before the given timeout, we can use assertTimeout().
Example 1 – In the below example, assertTimeout() is 2 sec, which means the assertion should be completed within 2 secs. We are waiting for 1 sec and then perform the assertion.
@Test
void assertTimeoutPositive() {
int a = 4;
int b= 5;
assertTimeout(
ofSeconds(2),
() -> {
// code that requires less then 2 seconds to execute
Thread.sleep(1000);
}
);
assertEquals(9, (a + b));
}
As the assertion is within the specified time of assertTimeout(), the timeout assertion passes and the test passes.
Example 2 – In the below example, assertTimeout() is 2 sec whereas are waiting for 5 sec and then performing the assertion.
@Test
void assertTimeoutNegative() {
int a = 4;
int b= 5;
assertTimeout(
ofSeconds(2),
() -> {
// code that requires less then 2 seconds to execute
Thread.sleep(5000);
}
);
assertEquals(9, (a + b));
}
As the assertion is outside the specified time of assertTimeout(), so the test fails. The assertion fails with an error message similar to: “execution exceeded timeout of 2000 ms by 3010 ms”.
Example 3 – In the below example, the assertion is mentioned just after
The executable will be executed in the same thread as that of the calling code. Consequently, execution of the executable will not be preemptively aborted if the timeout is exceeded.
@Test
void assertTimeoutNegative1() {
int a = 4;
int b= 5;
assertTimeout(
ofSeconds(2),
() -> {
// code that requires less then 2 seconds to execute
Thread.sleep(5000);
assertEquals(10, (a + b));
}
);
}
This shows that the assertion assertEquals() is still executed after the timeout also.
5. assertTimeoutPreemptively()
This assertion works just like assertTimeout(). When we want to assert that the execution of the supplied executable completes before the given timeout, we can use assertTimeoutPreemptively(). The only difference is that here the executable will be executed in a different thread than that of the calling code, whereas in assertTimeout() the executable will be executed in the same thread as that of the calling code. Furthermore, execution of the executable will be preemptively aborted if the timeout is exceeded here as contrary to assertTimeout() where the executable will not be preemptively aborted.
@Test
void assertPreemptiveTimeoutNegative() {
int a = 4;
int b= 5;
assertTimeoutPreemptively(
ofSeconds(2),
() -> {
// code that requires less then 2 seconds to execute
Thread.sleep(5000);
assertEquals(9, (a + b));
}
);
}
In this post, We saw that JUnit Jupiter comes with many of the assertion methods that JUnit 4 has and adds a few that lend themselves well to being used with Java 8 lambdas. All JUnit Jupiter assertions are static methods in the org.junit.jupiter.api.Assertions class.
Grouped assertions in JUnit 5 help make your tests cleaner and more readable by allowing you to group related assertions together with a single failure message if any of them fail. This is particularly useful when you want to ensure that multiple conditions are true in a given test scenario.
JUnit 5 supports an additional Assertion feature called Grouped assertions. In JUnit4, when you have to assert let’s say – 3 different conditions, then you need to write 3 different assertions and they will be executed sequentially. Imagine, 2nd assertion fails, then the program stops there and will not go to the 3rd assertion. To overcome this problem, JUnit5 has a new assertion called Grouped assertion that execute all the conditions present within it using assertAll(), irrespective of the fact if any assertion fails or not and generate a consolidated report with all the failures. To be honest, I love this feature and use very frequently.
All JUnit Jupiter assertions are static methods. They come from class:
org.junit.jupiter.api.Assertions
What is JUnit5?
JUnit 5 is composed of several different modules from three different sub-projects.
The JUnit Platform serves as a foundation for launching testing frameworks on the JVM. It also defines the Test Engine API for developing a testing framework that runs on the platform.
JUnit Jupiter is the combination of the new programming model and extension model for writing tests and extensions in JUnit 5. The Jupiter sub-project provides a Test Engine for running Jupiter-based tests on the platform.
JUnit Vintage provides a Test Engine for running JUnit 3 and JUnit 4 based tests on the platform. It requires JUnit 4.12 or later to be present on the classpath or module path.
To use JUnit5, add the Junit5 maven dependency to the POM.xml
Example 1 – The following example demonstrates positive assertions using `assertAll()` in JUnit 5. All assertions in this example are of the same type, specifically `assertEquals()`, grouped within an `assertAll()` assertion. The heading parameter for this group of assertions is “GroupedAssertionsWithSameAssertionType”.
@Test
void allPositive1() {
assertAll(
"GroupedAssertionsWithSameAssertionType",
() -> assertEquals(8, 5+3, "8 is not sum of 5 and 3"),
() -> assertEquals("java", "JAVA".toLowerCase()),
() -> assertEquals(16,4*4,"16 is not product of 4 and 4")
);
}
Result
As all 3 assertions pass, so the final result passes.
Grouped Assertions with Different Assertion Types
Example 2 – The following example demonstrates using `assertAll()` in JUnit 5 to group assertions of different types – assertEquals(), assertNotNull and assertNotEquals() within a single test. It consists of the heading parameter with the value “GroupedAssertionsWithDifferentAssertionType”.
@Test
void allPositive2() {
String str ="Spring";
assertAll(
"GroupedAssertionsWithDifferentAssertionType",
() -> assertEquals(8, 5+3, "8 is not sum of 5 and 3"),
() -> assertNotNull(str, () -> "The string should be null"),
() -> assertEquals("java", "JAVA".toLowerCase()),
() -> assertNotEquals(20,5*3,"20 is product of 5 and 3")
);
}
Result
As all 3 assertions pass, so the final result passes.
Grouped Assertions with multiple failure
Example 3– In the below example, out of 4 assertions, 3 assertions are failing, so the output will have the detail about all 3 assertion errors.
@Test
void allNegative() {
String str ="Spring";
Iterable<String> iterat1 = new ArrayList<>(asList("Java", "Junit4", "Test"));
Iterable<String> iterat2 = new ArrayList<>(asList("Java", "Junit5", "Test"));
assertAll(
"Negative-GroupedAssertionsWithDifferentAssertionType",
() -> assertIterableEquals(iterat1, iterat2),
() -> assertNull(str, () -> "The string should be null"),
() -> assertEquals("java", "JAVA"),
() -> assertNotEquals(20,5*3,"20 is product of 5 and 3")
);
}
Result
As one of the asserts in the group fails, instead of AssertionFailureErrorit results in MultipleFailuresError thereby displaying the heading of the grouped assertion passed as the input parameter i.e. Negative-GroupedAssertionsWithDifferentAssertionType in this example. This image shows all the 3 assertion failures.
Assertion 1 fails as we were expecting JUnit4, but response has JUnit5 Assertion 2 fails as the string was not NULL. Assertion 3 fails as Java is not equal to JAVA (case sensitivity).
Grouped Assertions Without Heading As Parameter
Grouped Assertions with Same Assertion Type
The assertAll () can be implemented without using the heading parameter. The below example is the same as the above one, we are just skipping the heading part.
@Test
void allNegative() {
String str ="Spring";
Iterable<String> iterat1 = new ArrayList<>(asList("Java", "Junit4", "Test"));
Iterable<String> iterat2 = new ArrayList<>(asList("Java", "Junit5", "Test"));
// In a grouped assertion all assertions are executed, and all failures will be reported together
assertAll(
() -> assertIterableEquals(iterat1, iterat2),
() -> assertNull(str, () -> "The string should be null"),
() -> assertEquals("java", "JAVA"),
() -> assertNotEquals(20,5*3,"20 is product of 5 and 3")
);
}
Result
The result displays without a heading.
Nested Or Dependent Grouped Assertions
Example with Outer assertAll() Passing and Nested assertAll() Executing
When one assertAll() includes one or more assertAll() then these are referred to as Nested or Dependent grouped assertions.
Example 1 – In this case, first, the assertAll() validates if the sum is correct or not. The sum is correct here, so the control moves to the nested assertAll() to verify all the assertions present within it.
@Test
void allDependentPositive() {
String str ="Spring";
// Within a code block, if an assertion fails the subsequent code in the same block will be skipped.
assertAll(
"DependentPositiveAssertions",
() -> {
assertEquals(8, 5 + 3, "8 is not sum of 5 and 3");
// Executed only if the previous assertion is valid.
assertAll("sub-heading",
() -> assertNotNull(str, () -> "The string should be null"),
() -> assertEquals("java", "JAVA".toLowerCase()),
() -> assertEquals(20, 5 * 4, "20 is product of 5 and 4")
); // end of inner AssertAll()
}
); // end of outer AssertAll()
}
Result
All the assertions within nested assertAll() are passes. So the final result passes.
Example of Outer AssertAll() Causing Nested AssertAll() Not to Execute
Example 2– In the below example, outer AssertAll() fails, so all the assertions within nested/dependent assertAll() are not executed.
@Test
void allDependentNegative() {
String str ="Spring";
// Within a code block, if an assertion fails the subsequent code in the same block will be skipped.
assertAll(
"DependentPositiveAssertions",
() -> {
assertEquals(8, 5 + 4, "8 is not sum of 5 and 3");
// Executed only if the previous assertion is valid.
assertAll("sub-heading",
() -> assertNull(str, () -> "The string should be null"),
() -> assertNotEquals("java", "JAVA".toLowerCase()),
() -> assertNotEquals(20, 5 * 4, "20 is product of 5 and 4")
); // end of inner AssertAll()
}
); // end of outer AssertAll()
}
Result
Example of Outer AssertAll() allows Nested AssertAll(), but nested AssertAll fails
Example 3 – In the below example, outer AssertAll() passes, so all the assertions within nested/dependent assertAll() are executed. But due to the failure of assertNull, the nested assertions are not evaluated, and the report will indicate the failure of `assertNull` first
@Test
void allDependentNegative1() {
String str ="Spring";
// Within a code block, if an assertion passes the subsequent code in the same block will be executed.
assertAll(
"DependentNegativeAssertions",
() -> {
assertEquals(8, 5 + 3, "8 is not sum of 5 and 3");
// Executed only if the previous assertion is valid.
assertAll("sub-heading",
() -> assertNull(str, () -> "The string should be null"),
() -> assertNotEquals("java", "JAVA".toLowerCase()),
() -> assertNotEquals(20, 5 * 4, "20 is product of 5 and 4")
); // end of inner AssertAll()
}
); // end of outer AssertAll()
}
Result
The nested assertions have failed. So they can be seen in the execution status.
In short,
When first assertAll() method passes, then all the subsequent assertions within that block will be executed and these assertions can further pass or fails.
When the first assertAll() assertion fails, then the execution of subsequent assertions is skipped.
That’s it! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
This tutorial explains the process to run the Rest API Tests in the GitLab pipeline. This is a significant step towards achieving CI/CD. Ideally, the tests need to run after any change (minor/major) before merging the change to the master branch. Suppose there are 100 changes in a day, and any QA won’t want to start the tests manually 100 times in a day. So, now adding tests to the GitLab pipeline comes into the picture. We can add a test stage to the pipeline and the tests will run automatically when the pipeline run, or we can schedule the tests to run automatically every hour or day using GitLab pipeline.
To use GitLab CI/CD, we need to keep 2 things in mind:-
a) Make sure a runner is available in GitLab to run the jobs. If there is no runner, install GitLab Runner and register a runner for your instance, project, or group.
b) Create a .gitlab-ci.yml file at the root of the repository. This file is where you define your CI/CD jobs.
Create a Rest API Project
Step 1 – Create a new Maven Project
Step 2 – Add dependencies to the project
Add the below-mentioned pom.xml which shows all the dependencies that need to add to the project.
It is needed to add maven-surefire plugin to run the TestNG tests through command line. To know more about this, please refer to this tutorial.
Step 3 – Create the Test Code to test the Rest API
Here, 2 tests are created. One of the tests gets all the employee data (GET) whereas another test creates an employee (POST).
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import org.json.JSONObject;
import org.testng.annotations.Test;
import io.restassured.http.ContentType;
public class RestAPIDemo {
@Test(description = "To get the details of employee with id 2", priority = 0)
public void verifyUser() {
// Given
given()
// When
.when().get("http://dummy.restapiexample.com/api/v1/employee/2")
// Then
.then().statusCode(200).statusLine("HTTP/1.1 200 OK")
// To verify booking id at index 2
.body("data.employee_name", equalTo("Garrett Winters"))
.body("message", equalTo("Successfully! Record has been fetched."));
}
@Test(description = "To create a new employee", priority = 1)
public void createUser() {
JSONObject data = new JSONObject();
// Map<String, String> map = new HashMap<String, String>();
data.put("employee_name", "APITest");
data.put("employee_salary", "99999");
data.put("employee_age", "30");
// GIVEN
given().baseUri("http://dummy.restapiexample.com/api").contentType(ContentType.JSON).body(data.toString())
// WHEN
.when().post("/v1/create")
// THEN
.then().statusCode(200).body("data.employee_name", equalTo("APITest"))
.body("message", equalTo("Successfully! Record has been added."));
}
}
Step 4 – Create testng.xml to run the tests through TestNG
Now, let’s create a testng.xml to run the TestNG tests. If JUnit is used instead of TestNG, then this step is not needed.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Test">
<classes>
<class name="com.example.RestAssured_TestNG_Demo.RestAPIDemo"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
Step 5 – Run the tests through the command line
Now, let us execute the tests through the command line. Go to the place where pom.xml of the project is placed and use the below command to run the tests. This step makes sure that all the tests are running as expected.
Step 8 – Create .gitlab-ci.yml file in the project in GitLab
It is a YAML file where you configure specific instructions for GitLab CI/CD. In the .gitlab-ci.yml, we can define:
The scripts you want to run.
Other configuration files and templates you want to include.
Dependencies and caches.
The commands you want to run in sequence and those you want to run in parallel.
The location to deploy your application.
Whether you want to run the scripts automatically or trigger any of them manually.
image: adoptopenjdk/maven-openjdk11
stages:
- test
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
test:
stage: test
allow_failure: true
# Run the tests
script:
- mvn $MAVEN_OPTS clean package
- mvn compile test
# Store artifacts
artifacts:
when: always
name: "report"
paths:
- target/surefire-reports/*
expire_in: 1 h
Step 9 – Run the tests in the GitLab pipeline
Now, when a new change is committed, a pipeline kicks off and it runs all the tests.
Step 10 – Check the status of the pipeline
Once the Status of the pipeline changes to either failed or passed.. that means the tests are already executed. Here, the pipeline is passed with brown colour means that the execution of the test is completed with some failures.
I have added an artifact in the gitalb-ci.yml with the name “report”. This artifact creates a folder with the name “report” and the reports in this folder come from the path /target/surefire-reports. This artifact gives us an option to download the reports or browse the report. This report will be available for 1 hour only as mentioned in the gitlab-ci.yml.
Step 11 – Download the report
Click on the Download button and the report zip file is downloaded. Unzip the folder, and it contains all different types of surefire-reports.
Example of Emailable-Report.html
Example of Index.html
Congratulations. This tutorial has explained the steps to run Selenium tests in GitLab CI/CD. Happy Learning!!
JUnit provides overloaded assertion methods for all primitive types and objects and arrays (of primitives or Objects). The parameter order is the expected value followed by an actual value. Optionally, the first parameter can be a String message that is output on failure. Only failed assertions are recorded. Assertions is a JUnit API or library of functions through which you can verify if a particular logic or condition returns true or false after execution of the test. If it returns false, then an AssertionError is thrown.
In order to increase the readability of the test and of the assertions itself, it’s always recommended to import statically the respective class.
import static org.junit.Assert.*;
Type of Assertions
1.assertEquals()
The assertEquals() assertion verifies that the expected and the actual values are equal. Below is an example of assertion pass.
@Test
public void equalsPositive() {
String expected = "JUnit";
String actual = "JUnit";
assertEquals("Expected and Actual Strings are not equal",expected,actual);
}
The output of the above program is
If expected and actual values are not equal, an AssertionError without a message is thrown. Below is an example of AssertionError thrown by assertEquals().
@Test
public void equalsNegative() {
String expected = "JUnit";
String actual = "JUnit Test";
assertEquals(expected,actual);
}
The output of the above program is
2. assertEquals with NULL
If expected and actual are NULL, then they are considered equal in assertEqual().
@Test
public void test2() {
String expected = null;
String actual = null;
assertEquals("Expected and Actual Strings are not null",expected,actual);
}
The output of the above program is
3. assertNotEquals()
The assertNotEquals() assertion verifies that the expected and the actual values are not equal. Below is an example where expected and actual values are not equal.
@Test
public void notEqualsPositive() {
String expected = "JUnit";
String actual = "JUnit5";
assertNotEquals("Expected and Actual Strings are equal",expected,actual);
}
The output of the above program is
If expected and actual values are equal, then an AssertionError with a message is thrown. Below is an example of AssertionError thrown by assertNotEquals().
@Test
public void notEqualsNegative() {
String expected = "JUnit";
String actual = "JUnit";
assertNotEquals("Expected and Actual Strings are equal",expected,actual);
}
The output of the above program is
4. assertArrayEquals()
If we want to assert that two arrays are equals, we can use the assertArrayEquals(). In the below example, two arrays are equal, so there is no AssertionError.
@Test
public void arrayEqualsPositive() {
char[] expected = {'j','u','n','i','t'};
char[] actual = {'j','u','n','i','t'};
assertArrayEquals("Expected and Actual Arrays are not equal",expected,actual);
}
The output of the above program is
Below is an example of AssertionError thrown by assertArrayEquals(), when arrays are different.
@Test
public void arrayEqualsNegative() {
char[] expected = {'J','U','n','i','t'};
char[] actual = "JUnit Test".toCharArray();
assertArrayEquals("Expected and Actual Arrays are not equal",expected,actual);
}
The output of the above program is
5. assertNull()
When we want to test if an object is nullwe can use the assertNull() assertion.
@Test
public void nullPositive() {
String str = null;
assertNull("String is not null",str);
}
The output of the above program is
If the object is not null, then an AssertionError is thrown with the given message. Below is an example of AssertionError thrown by assertNull().
@Test
public void nullNegative() {
String str = "Happy";
assertNull("String is not null",str);
}
The output of the above program is
6. assetNotNull()
If we want to assert that an object should not be null, we can use the assertNotNull assertion. Below is an example of object not null.
@Test
public void notNullPositive() {
String str = "Spring";
assertNotNull("String is null",str);
}
The output of the above program is
If the object is null, then an AssertionError is thrown with the given message. Below is an example of AssertionError thrown by assertNotNull().
@Test
public void notNullNegative() {
String str = null;
assertNotNull("String is null",str);
}
The output of the above program is
7. assertFalse()
If we want to verify that the condition is false, we can use assertFalse() assertion.
@Test
public void falsePositive() {
String str1 = "Happy Days";
String str2 = new String("Summer");
assertFalse("String 2 is not present in String 1", str1.contains(str2));
}
The output of the above program is
If the condition is true, then an AssertionError is thrown with the given message. Below is an example of AssertionError thrown by assertFalse().
@Test
public void falseNegative() {
String str1 = "Happy Days";
String str2 = new String("Happy");
assertFalse("String 2 is not present in String 1", str1.contains(str2));
}
The output of the above program is
8. assertTrue()
If we want to verify that the condition is true, we can use assertTrue() assertion.
@Test
public void truePositive() {
String str1 = "Happy Days";
String str2 = new String("Days");
assertTrue("String 2 is present in String 1", str1.contains(str2));
}
The output of the above program is
If the condition is false, then an AssertionError is thrown with the given message. Below is an example of AssertionError thrown by assertTrue().
@Test
public void trueNegative() {
String str1 = "Happy Days";
String str2 = new String("Healthy");
assertTrue("String 2 is present in String 1", str1.contains(str2));
}
The output of the above program is
10. assertSame()
The assertSame() internally uses operator == to validate if two objects are equal. Despite the two string values are the same, the below test fails. The reason is that the two object references are different.
In the below example, str1 and str2 have same value as well refer to the same memory addresses, so the assertion pass.
@Test
public void samePositive() {
String str1 = "Happy";
String str2 = str1;
assertSame("String1 and String 2 have different object reference",str1, str2);
}
The output of the above program is
Both the string objects str1 and str2 have the same value but are referring to the different memory addresses that result in the failure of the assert function.
@Test
public void sameNegative() {
String str1 = "Happy";
String str2 = new String("Happy");
assertSame("String1 and String 2 have different object reference",str1, str2);
}
The output of the above program is
11. fail()
The fail assertion fails a test throwing an AssertionFailedError. It can be used to verify that an actual exception is thrown or when we want to make a test failing during its development.
@Test
public void test14() {
String str1 = "Happy Days";
String str2 = new String("Happy");
Assert.fail("Fail this test");
}
The output of the above program is
What is the difference between assertEquals() and assertSame() assertions?
assertEquals() uses equals() method to compare objects, while assertSame() uses == operator to asserts that two objects refer to the same object.
In the below example, str1 and str2 both have the same value. So, the assertion passes here when we use assertEquals().
@Test
public void test10() {
String str1 = "Happy";
String str2 = new String("Happy");
Assert.assertEquals("String1 and String 2 are equal",str1, str2);
}
The output of the above program is
In the below example, both str1 and str2 have same value but different object reference, so the assertion fails with assertSame().
@Test
public void test11() {
String str1 = "Happy";
String str2 = new String("Happy");
Assert.assertSame("String1 and String 2 have different object reference",str1, str2);
}
The output of the above program is
In the below example, str1 and str2 have the same object reference. So, now the assertion passes with assertSame().
@Test
public void test12() {
String str1 = "Happy";
String str2 = str1;
Assert.assertSame("String1 and String 2 have different object reference",str1, str2);
}
In this tutorial, we will use the constructor injection technique to share web driver instances in multiple-step definitions using PicoContainer.
Why do we need Dependency Injection in Cucumber?
A new Framework is built that contains several Page Objects, Step Definitions, Feature files, and Helper Classes. Eventually, new Feature Files will be added that contain the steps that are already present in the existing Step Definition files. In this case, we will prefer to use the existing Step Definitions instead of creating new ones. But, Cucumber does not support Inheritance means it does not allow extending classes that contain Step Definitions or Hooks (@After, @Before, etc.). Now, Dependency Injection comes into the picture.
In Cucumber, if we want to share the state between multiple-step definition files, we will need to use dependency injection (DI). There are several options: PicoContainer, Spring, OpenEJB, etc. If you’re not already using DI, then it is recommended to use PicoContainer. Otherwise, use the one that’s already in use, because you should only have one.
To use PicoContainer, add the following dependency to the POM.xml
Imagine there are 2 feature files. These feature files are using the same browser initialization and website. Now, instead of creating the browser initialization twice for 2 feature files, why not create a Common Class and mention these details in that class and using DI, call this class in the main Step Definition classes.
Feature File 1 – HomePage.feature
Below is the example of feature file 1.
Feature: Home page validation
Background:
Given User Navigates to HRM login page
And User login with valid credentials
@ValidQuickLaunch
Scenario Outline: Login with valid credentials to check QuickLanuch options
When User is in Dashboard page
Then there are valid QuickLaunch options '<options>'
Examples:
| options |
| Assign Leave |
| Leave List |
| Timesheets |
@ValidLegendOptions
Scenario Outline: Login with valid credentials to check Manu Options
When User is in Dashboard page
Then there are valid Legend options '<legendOptions>'
Examples:
| legendOptions |
| Not assigned to Subunits |
| Administration |
| Client Services |
Feature File 2 – LoginPage.feature
Below is the example of feature file 2.
Feature: Login to HRM Application
@ValidCredentials
Scenario: Login with valid credentials
Given User is on Home page
When User enters username as "Admin"
And User enters password as "admin123"
Then User should be able to login sucessfully
Next, create a new class that holds the common data. For example:
public class ApplicationHooks {
private WebDriver driver;
@Before
public void setUp() {
setDriver();
}
public void setDriver() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.get("https://opensource-demo.orangehrmlive.com/");
}
public WebDriver getDriver() {
return driver;
}
@After
public void tearDown() {
getDriver().quit();
}
}
Then, in each of your step definition files that you want to use this common data, you can add a constructor that takes Step Data as an argument. This is where the injection occurs. For example:
LoginDefinition
public class LoginDefinition {
private ApplicationHooks hooks;
public LoginDefinition(ApplicationHooks hooks) {
this.hooks = hooks;
}
@Given("User is on Home page")
public void userOnHomePage() {
System.out.println("Home Page is opened");
}
@When("User enters username as {string}")
public void entersUsername(String userName) throws InterruptedException {
System.out.println("Username Entered");
hooks.getDriver().findElement(By.name("txtUsername")).sendKeys(userName);
}
@When("User enters password as {string}")
public void entersPassword(String passWord) throws InterruptedException {
System.out.println("Password Entered");
hooks.getDriver().findElement(By.name("txtPassword")).sendKeys(passWord);
hooks.getDriver().findElement(By.id("btnLogin")).submit();
}
@Then("User should be able to login sucessfully")
public void sucessfullLogin() throws InterruptedException {
String newPageText = hooks.getDriver().findElement(By.id("welcome")).getText();
System.out.println("newPageText :" + newPageText);
Assert.assertTrue(newPageText.contains("Welcome"));
}
}
HomeDefinition
public class HomePageDefinition {
ApplicationHooks hooks;
public HomePageDefinition(ApplicationHooks hooks) {
this.hooks = hooks;
}
@Given("User Navigates to HRM login page")
public void userOnHomePage() {
System.out.println("HRM login Page is opened");
}
@Given("User login with valid credentials")
public void entersCredentials() throws InterruptedException {
hooks.getDriver().findElement(By.name("txtUsername")).sendKeys("Admin");
hooks.getDriver().findElement(By.name("txtPassword")).sendKeys("admin123");
hooks.getDriver().findElement(By.id("btnLogin")).submit();
}
@When("User is in Dashboard page")
public void verifyDashboardPage() {
String dashboardTitle = hooks.getDriver().findElement(By.id("welcome")).getText();
Assert.assertTrue(dashboardTitle.contains("Welcome"));
}
@Then("there are valid QuickLaunch options {string}")
public void verifyQuickLinks(String options) throws InterruptedException {
switch (options) {
case "Assign Leave":
String linkOne = hooks.getDriver()
.findElement(By.xpath(
"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[1]/div/a/span"))
.getText();
Assert.assertEquals(linkOne, options);
break;
case "Leave List ":
String linkTwo = hooks.getDriver()
.findElement(By.xpath(
"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[2]/div/a/span"))
.getText();
Assert.assertEquals(linkTwo, options);
Thread.sleep(1000);
break;
case "Timesheets":
String linkThree = hooks.getDriver()
.findElement(By.xpath(
"//*[@id='dashboard-quick-launch-panel-menu_holder']/table/tbody/tr/td[3]/div/a/span"))
.getText();
Assert.assertEquals(linkThree, options);
break;
default:
break;
}
}
@Then("there are valid Legend options {string}")
public void verifyMenuOptions(String options) throws InterruptedException {
switch (options) {
case "Not assigned to Subunits":
String linkOne = hooks.getDriver()
.findElement(
By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[1]/td[2]"))
.getText();
Assert.assertEquals(linkOne, options);
break;
case "Administration":
String linkTwo = hooks.getDriver()
.findElement(
By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[2]/td[2]"))
.getText();
Assert.assertEquals(linkTwo, options);
break;
case "Client Services":
String linkThree = hooks.getDriver()
.findElement(
By.xpath("//*[@id='div_legend_pim_employee_distribution_legend']/table/tbody/tr[3]/td[2]"))
.getText();
Assert.assertEquals(linkThree, options);
break;
default:
break;
}
}
}
Create a Test Runner Class to execute the tests.
import org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
@RunWith(Cucumber.class)
@CucumberOptions(features= {"src/test/resources"}, glue= {"com.cucumber"})
public class RunCucumberTest {
}
Execute the tests either through JUnit Runner or Command-Line using maven.
The test Report can be accessed from the link provided in the execution status:
We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
This tutorial explains the process to run the Selenium Tests in the GitLab pipeline. This is a very important step towards achieving CI/CD. Ideally, the tests need to run after any change (minor/major) before merging the latest change to the master branch. Suppose there are 100 changes merged to the master branch in a day. It is expected to run the tests every time before deployment. In this case, any QA won’t want to start the tests manually 100 times in a day. Now, what should be done to overcome this problem. Now, adding tests to the GitLab pipeline comes into the picture. We can add a test stage to the pipeline and the tests will run automatically when the pipeline run.
Build the proposed changes. Then, push the commits to a feature branch. This branch should be in a remote repository that’s hosted in GitLab. The push triggers the CI/CD pipeline for your project. Then, GitLab CI/CD runs automated scripts (sequentially or in parallel) to build as well as to test the application. After a successful run of the test scripts, GitLab CI/CD deploys your changes automatically to any environment (DEV/QA/UAT/PROD). But if the test stage is failed in the pipeline, then the deployment is stopped.
After the implementation works as expected:
Get the code reviewed and approved.
Merge the feature branch into the default branch.
GitLab CI/CD deploys your changes automatically to a production environment.
To use GitLab CI/CD, we need to keep 2 things in mind:
a) Make sure a runner is available in GitLab to run the jobs. If there is no runner, install GitLab Runner and register a runner for your instance, project, or group.
b) Create a .gitlab-ci.yml file at the root of the repository. This file is where CI/CD jobs are defined.
The Selenium tests run on a headless browser in the pipeline.
What is a headless browser?
A headless browser is like any other browser, but without a Head/GUI (Graphical User Interface). A headless browser is used to automate the browser without launching the browser. While the tests are running, we cannot see the browser. However, we can see the test results coming on the console.
To explain, I have created 2 Selenium tests and used TestNG for asserting the tests. The tests will run on a headless Chrome browser. One more thing to keep in mind is that when tests run on a headless Chrome browser, the window screen won’t be full screen. So, the screen needs to be maximized explicitly otherwise some of the tests would fail.
Implementation Steps
Step 1 – Create a new Maven Project
Step 2- Add the dependencies to the POM.xml
Add the below-mentioned dependencies that need to add to the project to the pom.xml in Maven Project.
There are 2 different pages that need to be tested – LoginPage and ForgetPasswordPage
LoginPage contains the tests to log in to the application. After successful login, the application moves to the next webpage – HomePage. You can see that BaseTest class is extended in both the Test classes.
import org.openqa.selenium.By;
import org.testng.Assert;
import org.testng.annotations.Test;
@Test
public class ForgetPasswordPage extends BaseTest{
@Test
public void getHeading() {
Thread.sleep(1000);
driver.findElement(By.xpath("//*[@id='forgotPasswordLink']/a")).click();
String heading = driver.findElement(By.xpath("//*[@id='content']/div[1]/div[2]/h1")).getText();
System.out.println("Title : "+ heading );
Assert.assertEquals(heading, "Forgot Your Password?");
}
}
Step 4 – Create testng.xml to run the tests
Now, let’s create a testng.xml to run the TestNG tests. If JUnit is used instead of TestNG, then this step is not needed.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Test">
<classes>
<class name="org.example.DockerDemo.LoginPage"/>
<class name="org.example.DockerDemo.ForgetPasswordPage"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
Step 5 – Run the tests through command line
Now, let us execute the tests through the command line. Go to the place where the pom.xml of the project is placed and use the below command to run the tests. This step makes sure that all the tests are running as expected.
mvn compile test
GitLab Section
Step 6 – Create a blank project in GitLab
To know, how to create a blank new project in GitLab, please refer tothis tutorial.
Step 7 – Push the project from local repository to Gitlab Repository
To know, how to push the changes in GitLab, please refer to this tutorial.
Step 8 – Create a .gitlab-ci.yml file in the project in GitLab
There are many ways to create a new file in GitLab. One of the ways is to create a file as shown in the below image.
It is a YAML file where you configure specific instructions for GitLab CI/CD. In the .gitlab-ci.yml, we can define:
The scripts you want to run.
Other configuration files and templates you want to include.
Dependencies and caches.
The commands you want to run in sequence and those you want to run in parallel.
The location to deploy your application to.
Whether you want to run the scripts automatically or trigger any of them manually.
image: markhobson/maven-chrome
stages:
- test
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
test:
stage: test
allow_failure: true
# Run the tests
script:
- mvn $MAVEN_OPTS clean package
- mvn compile test
# Store artifacts
artifacts:
when: always
name: "report"
paths:
- target/surefire-reports/*
expire_in: 1 h
Image – markhobson/maven-chrome is used in this test. It is a docker image for Java automated UI tests.
This gitlab-ci.yml has only 1 stage. It is a test stage that contains the command to run the tests. It also creates an artifact that contains all the surefire reports. These reports can be saved as Test Evidence.
Step 9 – Run the tests in the GitLab pipeline
Now, when a new change is committed, a pipeline kicks off and it runs all the tests.
Step 10 – Check the status of the pipeline
Once the Status of the pipeline changes to either failed or passed.. that means the tests are already executed.
As you can see the Status is failed here which means that the execution is completed. Let us see the logs of the execution it shows that out of 2 tests, 1 test passed and 1 test failed. This shows that tests are running successfully in the GitLab pipeline.
As I have added an artifact also in the gitalb-ci.yml, which is highlighted in the image. This artifact creates a folder with the name “report” and the reports in this folder come from the path /target/surefire-reports. This artifact gives us the option to download the reports or browse the report. This report will be available for 1 hour only as mentioned in the gitlab-ci.yml.
Step 11 – Download the report
Once, will click on the download button, it will download “report.zip”. Unzip the folder and it looks like something as shown below:
Example of Emailable-Report.html
Example of Index.html
Congratulations. This tutorial has explained the steps to run Selenium tests in GitLab CI/CD. Happy Learning!!
A batch file (.bat) is used in DOS and Windows, which is an unformatted text file that consists of a series of commands to be executed by the command line interpreter.
Prerequisite:
Selenium
TestNG
Maven
Java 11
Maven Complier Plugin
Maven Surefire Plugin
Notepad
Let us first create some tests in a class.
import static org.testng.Assert.assertTrue;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class TestNGRunFromCommandLine {
WebDriver driver;
@BeforeTest
public void setUp() {
System.setProperty("webdriver.gecko.driver",
"C:\\Users\\Vibha\\Software\\geckodriver\\geckodriver.exe");
driver = new FirefoxDriver();
driver.get("https://opensource-demo.orangehrmlive.com/");
driver.manage().window().maximize();
}
@Test(description = "This test validates title of login functionality", priority = 0)
public void verifyLoginPage() {
String expectedTitle = driver.findElement(By.xpath("//*[@id='logInPanelHeading']")).getText();
System.out.println("Title :" + expectedTitle);
assertTrue(expectedTitle.equalsIgnoreCase("LOGIN Panel12"));
}
@Test(description = "This test validates successful login to Home page", priority = 1)
public void verifyHomePage() throws InterruptedException {
System.out.println("Username Entered");
driver.findElement(By.name("txtUsername")).sendKeys("Admin");
System.out.println("Password Entered");
driver.findElement(By.name("txtPassword")).sendKeys("admin123");
driver.findElement(By.id("btnLogin")).submit();
Thread.sleep(2000);
String newPageText = driver.findElement(By.xpath("//*[@id='content']/div/div[1]/h1")).getText();
System.out.println("newPageText :" + newPageText);
assertThat(newPageText, containsString("Dashboard"));
}
@AfterTest
public void teardown() {
driver.quit();
}
}
The below is the testng.xml file which has a class that we have created above and we will be invoking this xml file using batch file (.bat).
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Test">
<classes>
<class name="TestNG_Demo.TestNGRunFromCommandLine"/>
</classes>
</test>
</suite>
How to create a batch file?
Step 1: Open notepad Step 2: Paste the below lines of code – You may need to add your project location. In the example, project location is set as C:\Users\Vibha\Projects\Vibha_Personal\ParallelTestsTestNG Step 3: Save the file as ‘TestNGProject.bat’ in location that you want to save.
cd C:\Users\Vibha\Projects\Vibha_Personal\ParallelTestsTestNG
mvn compile test
Now, to run the tests, double-click on the TestNGProject.bat file and all the commands mentioned in the file will be executed one by one.
As we know, TestNG generates a lot of Reports automatically. We are going to look into 2 reports – emailable-report.html and index.html. The reports are generated under surefire-reports folder within the target directory.
Emailable-Report.html
Index.html
Hope this article helps you to invoke your tests using .bat file.
This tutorial explains the steps to disable infobar warning generated by Selenium for running tests in Chrome. Selenium tests run on Chrome shows a warning message – “Chrome is being controlled by automated test software as shown in the below image.“
We want to run the Selenium tests on Chrome, but without the above-shown warning message. This can be achieved by using excludeSwitches.
import java.util.Arrays;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import io.github.bonigarcia.wdm.WebDriverManager;
public class ChromeDisableInfobars {
public static void main(String[] args) {
WebDriverManager.chromedriver().setup();
// Create an object of Chrome Options class
ChromeOptions chromeOptions = new ChromeOptions();
// prevents Chrome from displaying the notification 'Chrome is being controlled
// by automated software'
options.addArguments("--disable-infobars");
chromeOptions.setExperimentalOption("excludeSwitches", Arrays.asList("enable-automation"));
// Create an object of WebDriver class and pass the Chrome Options object as an
// argument
WebDriver driver = new ChromeDriver(chromeOptions);
driver.get("https://duckduckgo.com/");
System.out.println("Title of Page :" + driver.getTitle());
// close the browser
driver.quit();
}
}
The previous tutorial has explained the DataProviders in TestNG. The DataProvider in TestNG is a way to pass the parameters in the test functions. Using DataProvider in TestNG, we can easily inject multiple values into the same test case. It comes inbuilt into TestNG and is popularly used in data-driven frameworks.
It is an option for the parallel execution of tests in TestNG.
It is advisable to create 2 classes – one class contains the Test cases and another class defines TestNG parameters – DataProviders.
Let us create a class for the DataProvider method with all the Test Data as shown below:
import org.testng.annotations.DataProvider;
public class DataProviderDemo {
@DataProvider(name = "testData", parallel=true)
public Object[][] dataProvFunc() {
return new Object[][] {
{"","","Username cannot be empty"},
{"","Test","Username cannot be empty"},
{"$%1234","2345%$","Invalid credentials"}
};
}
}
An extra parameter “parallel” is required to initiate parallel execution in TestNG using the data provider.
Below is the test which uses the parameter from the data provider and runs the tests parallelly.
A new ThreadLocal is instantiated for each test class, since it’s in the BeforeClass annotation.
private static final ThreadLocal<WebDriver> WEB_DRIVER_THREAD_LOCAL = new ThreadLocal<WebDriver>();
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite" thread-count="2" data-provider-thread-count="2">
<test name="Test">
<classes>
<class name="DataProvider.DataProviderParallelTests"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
In this file, the data-provider-thread-count is set to 2, then two browsers will be opened, and the first two tests will run from the list.
Run the test script from testng.xml, Right-Click on the XML, and select Run As ->TestNG Suite.
The execution status shown below shows that 2 threads are active at a time, which execute 2 sets of data provider parameters – Thread 14 and Thread 15. Once the tests are finished for Thread 14 and Thread 15, they are closed and a new Thread 15 is again initiated to start the test execution of the 3rd parameter.
TestNG generates multiple test reports under the folder test-output. We are mainly concerned about 2 reports – emailable-report.html and index.html.
Emailable-report.html
Emailable reports are a type of summary report that one can transfer to other people in the team through any medium.
Index.html
Index report contains the index-like structure of different parts of the report, such as failed tests, test files, passed tests, etc. We can divide this report into two parts. The left part contains the index, and this is the reason it is called an index report, while the right part contains the explored content of that index.
We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!