Rest-Assured : A BDD fashion Web Service testing framework

In this article we are going see how to use Rest Assure to test a web service.
For this example , we will be use a REST webservice with JSON format request.

What is webservice? 

For quick understanding, A web service is service over web to communicate over web technologies from device to device. This is one kind of messaging service. Details in wiki link
For this example, we will use a HTTP based REST web service where device communicate using JSON type data.

Example REST : 
To test a web service , we need a running one. We can test on public web services which are free to access. But, I am going to use one of my one. In my Github repository , you will find
Source : https://github.com/sarkershantonu/Bug-Storing-WS
Release : https://github.com/sarkershantonu/Bug-Storing-WS/releases

I am using this example so that you can download any release and run locally(avoid Internet access).

I will use release 2.0 which requires authentication.

How to run? 

Download any release and run as Jar file. From command line
Java -Jar bug-store-1.2-SNAPSHOT.jar

You can see, the webservice is running in @localhost on port 9100
Credentials are shantonu, 123456.

Now, Let's start the main project. I will do step by step project creation.
In this example, we are focus on testing only. Not much detail on reporting. If you want to add advance reporting, you may see my Allure blog post for detail reporting.

Required Software :
1. JDK 8
2. Maven 3.3.9
3. Any Java IDE

Step A : Create A maven project :  

1. Open your favorite IDE

2. Create a maven project with group & artifact id. I have used these
<groupId>org.automation</groupId>
<artifactId>webservice-test</artifactId>
<version>1.0-SNAPSHOT</version>

3. Add following dependencies for rest assure and Junit
<dependencies>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>rest-assured</artifactId>
        <version>${rest.assure.version}</version>
    </dependency>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>json-schema-validator</artifactId>
        <version>${rest.assure.version}</version>
    </dependency>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>rest-assured-common</artifactId>
        <version>${rest.assure.version}</version>
    </dependency>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>xml-path</artifactId>
        <version>${rest.assure.version}</version>
    </dependency>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>json-path</artifactId>
        <version>${rest.assure.version}</version>
    </dependency>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>spring-mock-mvc</artifactId>
        <version>${rest.assure.version}</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

Optional : if you cant access latest version , you can add the repository link

<repositories>
    <repository>
        <id>spring-milestones</id>
        <url>http://repo.spring.io/libs-milestone/</url>
    </repository>
</repositories>

Now, finally my pom.xml look like this.

Step 2 : Run WebService :

Download & Run The web service release jar from command line. You will see something like this.



As you can see, this is spring boot web service.When this webservice is started completely, you can see this line which means, the webservice is running in localhost 9100 port.


So, now we have a webservice running with these methods.

1.To see all bugs : GET : /table/bugs
2.To save a bug: POST : /table/bugs
3.To see a bug : GET : /table/bugs/{id}
4.To Update a Bug: PUT : /table/bugs/{id}
5.To Delete a Bug : DELETE : /table/bugs/{id}


Step 3 : Write your first test :

Now, lets get back to our IDE (I am using intellij IDEA)

we will start with creating a new bug entry in the web service. To do that, first I need a model class (or entity class) that represents a bug.

So, I add a class named Bug with 11 fields.

public class Bug  {
public Bug() {}
private Long id;
private String title;
private String summary;
private String description;
private String attachmentPath;
private String exceptions;
private String comments;
private String foundDateTime;
private String tags;
private String priority
private String servility;
} 

Now add setters, getters methods for each field.
And,  override equals(), hashcode() and toString() method.
For making life easy, I also added a constructor with all fields.
And a static method return a Bug object. So, finally this class becomes.

public class Bug {
    public Bug() {    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getSummary() {
        return summary;
    }
    public void setSummary(String summary) {
        this.summary = summary;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public String getAttachmentPath() {
        return attachmentPath;
    }
    public void setAttachmentPath(String attachmentPath) {
        this.attachmentPath = attachmentPath;
    }

    public String getExceptions() {
        return exceptions;
    }

    public void setExceptions(String exceptions) {
        this.exceptions = exceptions;
    }
    public String getComments() {
        return comments;
    }
    public void setComments(String comments) {
        this.comments = comments;
    }
    public String getFoundDateTime() {
        return foundDateTime;
    }

    public void setFoundDateTime(String foundDateTime) {
        this.foundDateTime = foundDateTime;
    }
    public String getTags() {
        return tags;
    }
    public void setTags(String tags) {
        this.tags = tags;
    }
    public String getPriority() {
        return priority;
    }
    public void setPriority(String priority) {
        this.priority = priority;
    }
    public String getServility() {
        return servility;
    }
    public void setServility(String servility) {
        this.servility = servility;
    }
    private Long id;
    private String title;
    private String summary;
    private String description;
    private String attachmentPath;
    private String exceptions;
    private String comments;
    private String foundDateTime;
    private String tags;
    private String priority;
    private String servility;

    @Override 
 public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Bug)) return false;
        Bug bug = (Bug) o;
        if (!getTitle().equals(bug.getTitle())) return false;
        if (getSummary() != null ? !getSummary().equals(bug.getSummary()) : bug.getSummary() != null) return false;
        if (getDescription() != null ? !getDescription().equals(bug.getDescription()) : bug.getDescription() != null)
            return false;
        if (getAttachmentPath() != null ? !getAttachmentPath().equals(bug.getAttachmentPath()) : bug.getAttachmentPath() != null)
            return false;
        if (getExceptions() != null ? !getExceptions().equals(bug.getExceptions()) : bug.getExceptions() != null)
            return false;
        if (getComments() != null ? !getComments().equals(bug.getComments()) : bug.getComments() != null) return false;
        if (getFoundDateTime() != null ? !getFoundDateTime().equals(bug.getFoundDateTime()) : bug.getFoundDateTime() != null)
            return false;
        if (getTags() != null ? !getTags().equals(bug.getTags()) : bug.getTags() != null) return false;
        if (!getPriority().equals(bug.getPriority())) return false;
        return getServility().equals(bug.getServility());
    }

    @Override 
 public int hashCode() {
        int result = getTitle().hashCode();
        result = 31 * result + (getSummary() != null ? getSummary().hashCode() : 0);
        result = 31 * result + (getDescription() != null ? getDescription().hashCode() : 0);
        result = 31 * result + (getAttachmentPath() != null ? getAttachmentPath().hashCode() : 0);
        result = 31 * result + (getExceptions() != null ? getExceptions().hashCode() : 0);
        result = 31 * result + (getComments() != null ? getComments().hashCode() : 0);
        result = 31 * result + (getFoundDateTime() != null ? getFoundDateTime().hashCode() : 0);
        result = 31 * result + (getTags() != null ? getTags().hashCode() : 0);
        result = 31 * result + getPriority().hashCode();
        result = 31 * result + getServility().hashCode();
        return result;
    }

    @Override 
 public String toString() {
        return "Bug{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", summary='" + summary + '\'' +
                ", description='" + description + '\'' +
                ", attachmentPath='" + attachmentPath + '\'' +
                ", exceptions='" + exceptions + '\'' +
                ", comments='" + comments + '\'' +
                ", foundDateTime='" + foundDateTime + '\'' +
                ", tags='" + tags + '\'' +
                ", priority='" + priority + '\'' +
                ", servility='" + servility + '\'' +
                '}';
    }

    public Bug(Long id, String title, String summary, 
String description, String attachmentPath, String exceptions, 
String comments, String foundDateTime, String tags, String priority, String servility) {
        this.id = id;
        this.title = title;
        this.summary = summary;
        this.description = description;
        this.attachmentPath = attachmentPath;
        this.exceptions = exceptions;
        this.comments = comments;
        this.foundDateTime = foundDateTime;
        this.tags = tags;
        this.priority = priority;
        this.servility = servility;
    }
    
    public static Bug getABug() {
        return new Bug(null, "title", "summary", "description"
"/home/shantonu", "RuntimeException", "from code", "Today",
 "rest assure", "NORM", "BLOCKER");
    }
}

 

I have added this Bug class under  package org.automation.model


Now, lets write our first test case. In test ,
1. Create a test package
2. Create an abstract test Base class (BugTestBase)
2. Create a test class which extends test base (BasicTests extends BugTestBase) . Like this




Why test base ? Following test organization best practices, i am using BugTestBase as test class common method and initialization items which i need for each test class before running. Now, define BugTestBase class like following

public abstract class BugTestBase {
    private static final String URL = "http://localhost:9100";
    private static final String user = "shantonu";
    private static final String pass = "123456";
    protected static final Long globalResponseTimeout = 2000l;
@BeforeClass 
public static void initClass(){
    RestAssured.baseURI = URL;
    RestAssured.basePath="/table/bugs/";
}}

You can see, I am assigning base URL, primary path for BUG web service.

Now, lets open BasicTests class and add following test method

@Test 
public void testAddOne_validateResponse(){
    Bug aBug = Bug.getABug();
    given().
            auth().basic(user,pass).
            contentType(ContentType.JSON).body(Bug.getABug(),ObjectMapperType.JACKSON_2).
            post().then().assertThat().
            statusCode(HttpStatus.SC_CREATED).
            contentType(ContentType.JSON).
            header("Content-Type", "application/json;charset=UTF-8").
            body("title",equalTo(aBug.getTitle())).
            time(lessThan(globalResponseTimeout));
}

 Now, we see what we are doing here. (important part to understand rest assured)

1. Creating a Bug instance
2. Initiating a HTTP POST request with this bug instance.
3. When we are putting this request , we are converting in JSON with Rest Assure built in object mapper which usages JackSon_2 type mapper.
4. We are declaring the content type as JSON which will be included in request header.
5. We are hitting URL (localhost:9100/table/bugs) which is coming from BugTestBase . So, no need extra path in post().
6. After posting request, we get response and validating response code, header item , content type, body title=the bug's title. And finally the response time is less than 2s.

Now, run the test case either from IDE or maven command. We will see test pass



Now, this proves the test has pass and created a bug entry. If you want to validate , if it is create or not, you can use Postman chrome extension (or any other REST client) to check.
From bug webservice URLs , we can see there is GET /table/bugs request which shows all stored bugs. So, i use this url in post man and send the request, I can see stored bug as JSON response.





So, the bug is added and we can see it.

Now, as we know, for testing if we add any new data we should cleanup. So, this test case does not clean up. This was purpose of validating response. Now, create another test case for creating a bug , validate that it is exact same match with the bug we tried to add and finally delete from DB entry.

@Test
 public void testAddOne_validateResponseObject() {
    Bug request = Bug.getABug();
    Bug responsedBug = given().
            auth().basic(user,pass).
            contentType(ContentType.JSON).
            body(request,ObjectMapperType.JACKSON_2).
            post().as(Bug.class);
    Assert.assertTrue(request.equals(responsedBug));// validating responseded item is equal to what i put in    System.out.println(responsedBug.toString());// optional, to view purpose
    //cleanup my data    given().auth().basic(user,pass).
            delete(responsedBug.getId().
                    toString()).then().
            assertThat().statusCode(HttpStatus.SC_NO_CONTENT);

}

So, what is happening here which is not same like as previous one?
1. We are not validating http responses, but we are converting into Bug object (responsedBug).
2. Comparing two bug which we send, which we get
3. Deleting the response bug with its id using exposed URL DELETE : /table/bugs/{id}
4. When deleting, we are also checking http status for successful deletion.

Step 5: Write others tests :

1. See all bugs :
 
@Test 
public void testViewAll() {
   given().
           auth().basic(user,pass).
           get().
           then().assertThat().
           statusCode(HttpStatus.SC_OK).
           contentType(ContentType.JSON).
           header("Content-Type", "application/json;charset=UTF-8").
           time(lessThan(globalResponseTimeout));
}

=>Destination URL(GET ) = localhost:9100/table/bugs
=>We are checking Http status, response type, content header, time.

2. View a bug :
 
@Test 
// assuming that bug with ID 1 present. 
 
public void testViewABug(){
    given().
            auth().basic(user,pass).
            get(Integer.toString(1)).then().assertThat().
            statusCode(HttpStatus.SC_OK).
            contentType(ContentType.JSON).
            header("Content-Type", "application/json;charset=UTF-8").
            body("id", equalTo(1));

}

=>Destination URL(GET ) = localhost:9100/table/bugs/1
=>We are checking Http status, response type, content header, and response body should have id = 1, which we are requesting.


3. Update a bug :

@Test 
public void testUpdateeABug(){
    Bug createdbug =
            given().
                    auth().basic(user,pass).
                    contentType(ContentType.JSON).
                    body(Bug.getABug(),ObjectMapperType.JACKSON_2).
                    post().as(Bug.class);

    createdbug.setTitle("This is modified title");
    createdbug.setDescription("This is modified description");

    given().
            auth().basic(user,pass).
            contentType(ContentType.JSON).body(createdbug,ObjectMapperType.JACKSON_2).
            when().put(createdbug.getId().toString()).
            then().assertThat().
            statusCode(HttpStatus.SC_ACCEPTED).
            contentType(ContentType.JSON).
            header("Content-Type", "application/json;charset=UTF-8").
            body("id",equalTo(createdbug.getId().intValue()));
    //cleanup    given().
            auth().basic(user,pass).
            delete(createdbug.getId().toString()).
            then().assertThat().
            statusCode(HttpStatus.SC_NO_CONTENT);
}
 
=>Destination URL(PUT) = localhost:9100/table/bugs/1
=>We are Creating a bug
=>From response, we get a bug and change its title & description.
=>We send update request for this the bug with same ID.
=>We are checking Http status, response type, content header, and response body should have id = same as requested bug's id
=>Finally we are deleting the bug & checking its status for cleaning up.

4. Delete a bug :
//Add a bug & delete that bug with ID and check http status
@Test
public void testDeleteABug(){
    Bug respnsebug =
            given().
                    auth().basic(user,pass).
                    contentType(ContentType.JSON).
                    body(Bug.getABug(),ObjectMapperType.JACKSON_2).
                    post().as(Bug.class);
    given().auth().basic(user,pass).
            delete(respnsebug.getId().toString()).
            then().assertThat().
            statusCode(HttpStatus.SC_NO_CONTENT);

}

=>Destination URL(DELETE ) = localhost:9100/table/bugs/1
=>We are creating a bug and deleting that bug. 
=>We are checking Http status only.

Now, if you are sending same request , we will see http 500. This examples only have happy paths to test. You can add all of the exception cases and boundary values by looking at the web service source code here.

So, finally if we run all the test case together, we will be looking like this.



Now, you may say, these are not all. The main purpose of this post is to help on getting start testing with Rest Assured. I will be gradually adding more example to the repository. I have added small Response utilities already. So, more item will come gradually.

Note : if you use my webservice version 1.0, please do not use this line for authentication

auth().basic(user,pass).

So , Here is the Github URL : https://github.com/sarkershantonu/Automation-Getting-Started/tree/master/RestAssured

And detail wiki page : https://github.com/rest-assured/rest-assured/wiki/usage
 
Please comment if you have any query.
 


----- Thanks.. :)



Data driven testing with Junit & easytest

In this article we are going to see how can we make a Data Driven testing with Junit. For this I am going to use a library called EasyTest.
For TestNG , as we know, it has built in data provider. Using easy test, we cam use Junit to do data driven tests.

What is Data Driven Testing ?
When you testing is driven by your data then it refers to data driven testing. Formal definition can be found in wiki. 
In summary , your input data, expected output, configuration etc will be defined parameterized.  So, in the end, you do not need to change your tests but with change of data, you can increase number of tests as well as coverage. That means, your data drives your testing capacity as well as quality.

This is very good approach when you need to deal with large amount of data with different permutation and combinations.

Where I use this approach? 

1. When I need to check big number of data and its out put against DB query or web api methods(REST/SOAP).
2. When I need to drive the same UI tests with different data combinations.
3. When I need to isolate date changing behaviors over configuration changes.

How we are going to achieve?

We are going to resolved this by parameterized tests. And this parameters will take values(test data) form our defined files. This is the missing part from TestNG with Junit. We will resolve using EasyTest library.

Note : This is not the only cause we are using Easy test. Easy test also have a lot of impressive features. We will see one by one. Here is the easy test project link in github.

Let's learn with Example :
For learning , I am using simple calculator class (Github link.). Just adding two numbers and expecting a result all in Double type.


public class Calculator {
    public Double add(Double a, Double b){
        return a+b;
    }
}

And, lets make a test case without Easy Test.

public class SimpleTest extends DataDrivenTest{
    @Test    public void testAddition(){
        Assert.assertEquals(25.5,calculator.add(10.5,15.0),0.01);
    }
}


We will evolve this simple test case using Easy Test.So, lets start with making a project.

Step A : Creating Maven Project :
1. Create a maven project with you favorite group id and artifact id. (I used org.automation & datadriven)
2. Include following Dependencies.

For Junit (as we are using)
dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

For Easy Test
<dependency>
    <groupId>org.easetech</groupId>
    <artifactId>easytest-core</artifactId>
    <version>1.4.0</version>
</dependency>

And for logging (this is optional)
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.21</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.21</version>
</dependency>

[This will be enough to use easy test internal logging. ]

Now, to provide data parameter, we need to include our data files. As best practice, i will put all of my data as resources. So, i need to include as resource in pom. So, finally project pom.xml looks like this

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          
xmlns="http://maven.apache.org/POM/4.0.0"          
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.automation</groupId>
    <artifactId>datadriven</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--Only mandatory part : start--> 
        <dependency>
            <groupId>org.easetech</groupId>
            <artifactId>easytest-core</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--Only mandatory part: End -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/test/resources</directory>
                <includes>
                    <include>**/*</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>


Step B :Creating Data Files : Easy test supports
1. Excel : Office 97/2003 format (xls extension)
2. CSV : Comma Separated Value
3. XML
4. JSON format via CSV files.
Note : Easy test also supports custom data types (we will skip this part to make it simple in blog, you can see example in Github)

For CSV and EXCEL follow these rules
1. First row , first column will be Method name (so, each row of this column will be blank)
2. First row From 2nd column, all will be parameter variable name.
3. All row of column where method name is written will be blank.

CSV : 



Excel (.xls) :

 

XML : 



 You can see all of my files from this in github.

Now, Let's load data from different data loader.

To load data, commonly we are using annotations
1. @DataLoader to define the source of the file.
2. @Param to define which column data will be consider as item to get.

As this annotations are specific to Easy Test, we need to run this test class with DataDrivenTestRunner.class which is provided by

So, first we see CSV data loader.

@RunWith(DataDrivenTestRunner.class)
public class CSVLoaderExample extends DataDrivenTest {

    @Test    @DataLoader(filePaths = "calculator.csv", loaderType = LoaderType.CSV)
    public String testAddFromCSV(@Param(name = "a") Double a,
                                 @Param(name = "b") Double b,
                                 @Param(name = "expected") Double expected) {
        Assert.assertEquals(expected, calculator.add(a, b), 0.1);
        return "success";

    }
    @Test    @DataLoader(filePaths = "calculator2.csv")
    public void testAdd(@Param(name = "a") Double a, @Param(name = "b")Double b, @Param(name = "expected")Double expected){
        Assert.assertEquals(expected, calculator.add(a,b),0.1);
    }
}

In here , you can see, I am
=> running the test with DataDrivenTestRunner.class
=> loading calculator.csv (and other one also)
=> getting parameter with name a, b, expected.

Here is the CSV file content look like
testAddFromCSV,a,b,expected
 ,15.0,25.0,40
 ,15.0,25.0,40
 ,15.0,25.0,40
 ,15.0,25.0,40
 ,15.0,25.0,40
 ,15.0,25.0,40
 ,15.0,25.0,40
 ,15.0,25.0,40
 ,15.0,25.0,40
 ,15.0,25.0,40
 ,900.0,250.0,1150.0 


Now, you may ask how row is handled. Easy test consider each row as one record and it will iterate our tests based on number of row present in the data file. So, define column for input data is more than enough.

Best Practice : I used to follow a single file to provide input data as well as expected output. You may use separate file for that.

In the same way, if we look at excel data loader :

@RunWith(DataDrivenTestRunner.class)
public class ExcelLoaderExample extends DataDrivenTest {
    @Test    @DataLoader(filePaths = "calculator.xls", loaderType = LoaderType.EXCEL)
    public void testAddFromExcel(@Param(name = "a") Double a, @Param(name = "b") Double b, @Param(name = "expected") Double expected) {
        Assert.assertEquals(expected, calculator.add(a, b), 0.1);
    }
    @Test    @DataLoader(filePaths = {"calculator2.xls"})
    public void testAdd(@Param(name = "a") Double a, @Param(name = "b")Double b, @Param(name = "expected")Double expected){
        Assert.assertEquals(expected, calculator.add(a,b),0.1);
    }

}


And XML data loader

@RunWith(DataDrivenTestRunner.class)
public class XMLLoaderExample extends DataDrivenTest {

    @Test    @DataLoader(filePaths = "calculator2.xml", loaderType = LoaderType.XML)
    public String testAddXMLOutput(@Param(name = "a") Double a, @Param(name = "b") Double b, @Param(name = "expected") Double expected) {
        Assert.assertEquals(expected, calculator.add(a, b), 0.1);
        return "success";

    }
}

Note : 
1. File paths contains physical path , if the file is in classpath, file name is enough. So, when I am defining my pom.xml resources , we should include our data file to make it work like this. Or we have maintain proper path for input.
2. If you use single file loading, you may avoid LoaderType parameter.
3. When you are using multiple same type file to load, make sure you do not have same column name. If same, columns form 2nd or later file will be honored. (Lifo way, last file will be honored)
4. Does not supports different type of loader in same method. So, you can not load Excel & CSV for the same method with single data loader. Only first one will be working.
5. Single method/class does not supports multiple data loader annotation.
6. Method level data loader will overload class level data loader.
7. Use class level data loader when you are using a single file for multiple test method's parameters.
8. In param, data used convered in Long, String, Double format. But we can use custom data type with our own parsers. Easy test has interface for this. (use AbstractConverter<YourDataType> )
9. If we need to customize this parameter data showing, we can use @Display annotation when with loader. you can see an example here.

Here are some examples for multiple data loader :
@RunWith(DataDrivenTestRunner.class)
public class MultipleDataLoaderExample extends DataDrivenTest {

    // only the first loader will be working..    // in here "calculator2.csv"    @Test    @DataLoader(filePaths = {"calculator2.csv","calculator3.xml"})
    public void testAdd(@Param(name = "a") Double a, @Param(name = "b")Double b, @Param(name = "expected")Double expected) {
        Assert.assertEquals(expected, calculator.add(a, b), 0.1);
    }
}

@RunWith(DataDrivenTestRunner.class)
public class MultipleDataLoaderExampleSameType extends DataDrivenTest{

@Test 
@DataLoader(filePaths = {"calculator3.csv","calculator2.csv"})//calculator2 is accepted not 2=> why, it honors the last item, top item of the array list of files 
 public void testAdd(@Param(name = "a") Double a, @Param(name = "b")Double b, @Param(name = "expected")Double expected){
        Assert.assertEquals(expected, calculator.add(a,b),0.1);
    }

}

So, you may see more detail examples from here.

Now, beside Data loading , Easy test has lot of other features. I am not going details but I have examples for each one. So, I am adding one by one with small explanation.

Reporting : Easy test provide really easy reporting... :). Just use annotation. Here are some examples.

Default Report : (type PDF & stored in user directory)
 
@RunWith(DataDrivenTestRunner.class)
@Reportpublic class DefaultReportExample extends DataDrivenTest{
    @Test    @DataLoader(filePaths = "calculator2.xls")
    public void testAdd(@Param(name = "a") Double a, @Param(name = "b") Double b, @Param(name = "expected") Double expected) {
        Assert.assertEquals(expected, calculator.add(a, b), 0.1);
    }

}



Report with class path changing in classpath :  (reports stored in build directory , target folder in maven) . A folder name TestReports will be created

@RunWith(DataDrivenTestRunner.class)
@Report(outputLocation = "classpath:TestReports")
public class ClassPathExampleReport extends DataDrivenTest{

    @Test    @DataLoader(filePaths = "calculator.xls")
    public void testAddFromExcel(@Param(name = "a") Double a, @Param(name = "b") Double b, @Param(name = "expected") Double expected) {
        Assert.assertEquals(expected, calculator.add(a, b), 0.1);
    }
    @Test    @DataLoader(filePaths = "calculator2.xls")
    public void testAdd(@Param(name = "a") Double a, @Param(name = "b") Double b, @Param(name = "expected") Double expected) {

        Assert.assertEquals(expected, calculator.add(a, b), 0.1);
    }
}




Report with class path changing in folder path : (we specify in our file system)

@RunWith(DataDrivenTestRunner.class)
@Report(outputLocation = "file:TestReports")// default location = project working directorypublic class CustomPathExampleReport extends DataDrivenTest{

    @Test    @DataLoader(filePaths = "calculator.xls")
    public void testAddFromExcel(@Param(name = "a") Double a, @Param(name = "b") Double b, @Param(name = "expected") Double expected) {
        Assert.assertEquals(expected, calculator.add(a, b), 0.1);
    }
    @Test    @DataLoader(filePaths = "calculator2.xls")
    public void testAdd(@Param(name = "a") Double a, @Param(name = "b") Double b, @Param(name = "expected") Double expected) {
        Assert.assertEquals(expected, calculator.add(a, b), 0.1);
    }
}

 

In the github repository, i have given more example which are simple to understand. Here are the important notes.
1. There are two type of report functional test report and performance test report(includes time to run tests). We can create multiple report type.
2. Report making is slow, so the time of report making will be included as test execution time
3. There are 3 type of report file format. Excel, PDF & HTML where pdf is the default choice. We can create multiple report type format.
4. @Report can be used in class level which means, when generated, it includes all test method results.
5. Report location can be stored specific file path or in build directory, class path. Class path report will be cleared when we use mvn clean, so choose carefully.

A pdf report sample :




Parallel Threads : Easy test has a simple annotation @Parallel where we can define how many thread JVM will allocate to test for a test class.

@Parallel(threads = 5)//this is very fragilepublic class ParallelTestExample extends DataDrivenTest_withDefaultAnnotation {

    @Test    @DataLoader(filePaths = "calculator.xls", loaderType = LoaderType.EXCEL)
    public void testAddFromExcel(@Param(name = "a") Double a, @Param(name = "b")Double b, @Param(name = "expected")Double expected){
        Assert.assertEquals(expected, calculator.add(a,b),0.1);
    }

}

You can see, this test will run with 5 threads.

We can run our test suit parallely also woth ParallelSuit

@RunWith(Suite.class)
@ParallelSuite(threads = 3)
@Suite.SuiteClasses({RepeatExample.class, TestWithPolicyExample.class})
public class ParallelSuitExample {
}


Note : this is very frajile. It might create resource alocation errors. And easy test does not ensures these are concurrent.

Test Repetation : In Easy Test , we can repeat a test method with annotation @Repeat.

@RunWith(DataDrivenTestRunner.class)
@TestPolicy(PolicyExample.class)
public class RepeatExample extends DataDrivenTest {
    @Test    @Repeat(times = 5)
    public void testAddFromExcel(@Param(name = "a") Double a,
                                 @Param(name = "b") Double b,
                                 @Param(name = "expected") Double expected) {
        Assert.assertEquals(expected, calculator.add(a, b), 0.1);
    }
}

It's a serially running, not parallel. 

Test Property : Easy test has a nice annotation  @TestProperties which can be used to directly inject test property from class path.
 
public class TestPropertiesExample extends DataDrivenTest_withDefaultAnnotation {
    @TestProperties(value = {"test.properties"})
    private Properties myProps;

    @Test    public void testAddition() {
        Assert.assertEquals("Easy Test Demos", myProps.getProperty("test.title"));
    }
}

[Ignore the extends part, that is not needed. To keep test nice, i used. you can see form github sources. ]

Test Policy : Test policy is very useful thing in Easy test where we can define test policy in a seperate class and use it in test class.

For this define a policy class. 

@Ignore@Parallel(threads = 2)
@Report(reportTypes = {Report.REPORT_TYPE.DEFAULT,
        Report.REPORT_TYPE.METHOD_DURATION},
        outputFormats = Report.EXPORT_FORMAT.PDF,
        outputLocation = "file:TestReports")
@DataLoader(filePaths = "calculator.xls")// i preffer data loder should be with method@Display(fields = "id")
public class PolicyExample {
}

And Use it in a test class
@RunWith(DataDrivenTestRunner.class)
@TestPolicy(PolicyExample.class)
public class TestWithPolicyExample extends DataDrivenTest {

    @Test    public void  testAddFromExcel(@Param(name = "a") Double a, @Param(name = "b") Double b, @Param(name = "expected") Double expected) {
        Assert.assertEquals(expected, calculator.add(a, b), 0.1);
    }
}

So, we can see, the policy will define data file + reporting and parallel threads for the threads.

Where to use? 
=> While testing , if we feel to have test configuration separately.
=> When we have seperate testing configuration to run in different situation. Like test in dev PC , or in CI, or for test reporting , or differenty type of testing, etc.

Cons : 
1. Error messages are not user friendly. So debugging is not easy. you need to know easy test architecture to understand errors.
2. Paralle execution might cause error related resource locked or busy.
3. Blank character in excel or  CSV might cause error. So, be careful while creating data file.
4. There are some known bugs present. One of popular bug is, test fail report only generated with excel data loader. Other data loader can not generate fail test reports(only pass test report generated).

I hope this post will help to use Easy test. My Github Example : https://github.com/sarkershantonu/Automation-Getting-Started/tree/master/junit-easytest

Todo : Custom Data Type Converter Example. I will add gradually.

I love to have comments in my blog.
----- Thanks.. :)