Translate

Tuesday, April 23, 2019

Consuming REST APIs With React.js

Source: https://dzone.com/articles/consuming-rest-api-with-reactjs

In this post, we go through a tutorial on how to create a React application that can perform CRUD functions and take in data from a RESTful API.

Recently we have experienced rapid growth of mobile, Internet-based communication. We have smartphones, tablets, netbooks, and other connected devices that create a need to serve appropriate content for each specific front-end profile. Also, the Internet is becoming available for new regions and social groups, constantly increasing web traffic. On the other side, users' computers and browsers, and JavaScript itself are getting more and more powerful, providing more possibilities for processing data on the web via the client-side. In this situation, the best solution can often be to send data as the response, instead of sending page content. That is, we don't need to reload the whole page on each request, but send back the corresponding data and let the client (front-end stuff) process the data.
We can develop a backend application exposing a remote API (usually based on the REST protocol) and a front-end (usually JavaScript) application, which communicates with the API and renders all the data on the device.
If backend data is consumed by humans, we need to develop a user interface (UI) to provide the possibility for users to manage the data. Modern web applications should have responsive and friendly UIs, ensuring adequate user experience. Also, modern UIs can be arbitrarily complex, with multi-panel, nested layouts, paging, progress bars, etc. In this case, the component model can be the right solution. React.js is a light-weight JavaScript framework, which is oriented toward the creation of component-based web UIs. React doesn't provide any means for communicating with the backend, but we can use any communication library from inside React components.
As an example, we can develop a simple React application consuming the REST API we created in a previous article. The API provides methods to use an online collection management application. Now our task is to develop a web UI to use these methods.
Before starting development, we need to set up a React.js development environment.

1. React.js Development Environment Set Up

There are several ways to use React.js. The simplest way is just to include React libraries in the <script> tags on the page. 
Listing 1.1. Including the React.js library in the HTML page:
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <div id="hello_container" class=""></div>
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script>
      class Hello extends React.Component {
        constructor(props) {
          super(props);
        }
        render() {
          return React.createElement(
            'div',
            null,
            `Hello ${this.props.name}!`
          );
        }
      }
      ReactDOM.render(React.createElement(Hello, {name: 'React'}, null), document.querySelector('#hello_container'));
    </script>
  </body>
</html>
This way, we can very quickly start developing React applications, but we cannot use certain advanced features, like JSX, for example. So, a more appropriate solution, especially for large and sophisticated applications, would be to use the  create-react-app tool. To install it, you need Node.js and npm to be installed on your computer: npm install -g create-react-app 
Then you can run the following command in the root directory, where you want to create your project:
 .../project-root>create-react-app consuming-rest
This command creates a new folder ('consuming-rest') with a ready-to-run prototype React application.
Now we can enter the directory and run the application, as follows:
 .../project-root>cd consuming-rest
.../project-root/consuming-rest>npm start
 This starts the application in a new browser at http://localhost:3000:
React.js application
It is a trivial but completely functional front-end application, which we can use as a prototype for creating our UI.
Initially, we can implement a data service to communicate with the server.

2. Backend Communication Service Implementation

In general, it is a good idea to put all related functionalities in one place. Putting our functionality behind a service which exposes certain APIs ensures more flexibility and testability for our application. So, we create a communication service class, which implements all basic CRUD operations for data exchange with the server and exposes these operations as methods for all React components. To make our UI more responsive, we implement the methods as asynchronous. Provided the API is unchanged, we can change the implementation freely and none of the consumers will be affected. To put these concepts into practice, let's create a mock implementation of the service, which provides mock data for building and testing our UI. Our mock service can look like this.
Listing 2.1.  src/shared/mock-item-service,js – mock ItemService:
class ItemService {
  constructor() {
    this.items = [
      {link:1, name:"test1", summary:"Summary Test 1", year:"2001", country:"us", price:"1000", description:"Desc 1"},
      {link:2, name:"test2", summary:"Summary Test 2", year:"2002", country:"uk", price:"2000", description:"Desc 2"},
      {link:3, name:"test3", summary:"Summary Test 3", year:"2003", country:"cz", price:"3000", description:"Desc 3"},
    ];
  }
  async retrieveItems() {
      return Promise.resolve(this.items);
  }
  async getItem(itemLink) {
    for(var i = 0; i < this.items.length; i++) {
      if ( this.items[i].link === itemLink) {
        return Promise.resolve(this.items[i]);
      }
    }
    return null;
  }
  async createItem(item) {
    console.log("ItemService.createItem():");
    console.log(item);
    return Promise.resolve(item);
  }
  async deleteItem(itemId) {
    console.log("ItemService.deleteItem():");
    console.log("item ID:" + itemId);
  }
  async updateItem(item) {
    console.log("ItemService.updateItem():");
    console.log(item);
  }
}
export default ItemService;
Based on this, we can build the UI.

3. CRUD UI Implementation

React supports component hierarchies, where each component can have a state and the state can be shared between related components. Also, each component's behavior can be customized by passing properties to it. So, we can develop the main component, which contains the list of collection items and works as the placeholder for displaying forms for corresponding CRUD actions. Using the stuff generated by the create-react-app tool, we change the content of app.js as follows.
Listing 3.1. src/App.js – the main component as the application frame:
import React, { Component } from 'react';
import './App.css';
import ItemDetails from './item-details';
import NewItem from './new-item';
import EditItem from './edit-item';
import ItemService from './shared/mock-item-service';
class App extends Component {
  constructor(props) {
    super(props);
    this.itemService = new ItemService();
    this.onSelect = this.onSelect.bind(this);
    this.onNewItem = this.onNewItem.bind(this);
    this.onEditItem = this.onEditItem.bind(this);
    this.onCancel = this.onCancel.bind(this);
    this.onCancelEdit = this.onCancelEdit.bind(this);
    this.onCreateItem = this.onCreateItem.bind(this);
    this.onUpdateItem = this.onUpdateItem.bind(this);
    this.onDeleteItem = this.onDeleteItem.bind(this);
    this.state = {
      showDetails: false,
      editItem: false,
      selectedItem: null,
      newItem: null
    }
  }
  componentDidMount() {
      this.getItems();
  }
  render() {
    const items = this.state.items;
    if(!items) return null;
    const showDetails = this.state.showDetails;
    const selectedItem = this.state.selectedItem;
    const newItem = this.state.newItem;
    const editItem = this.state.editItem;
    const listItems = items.map((item) =>
      <li key={item.link} onClick={() => this.onSelect(item.link)}>
         <span className="item-name">{item.name}</span> |  {item.summary}
      </li>
    );
    return (
      <div className="App">
          <ul className="items">
            {listItems}
          </ul>
          <br/>
          <button type="button" name="button" onClick={() => this.onNewItem()}>New Item</button>
          <br/>
            {newItem && <NewItem onSubmit={this.onCreateItem} onCancel={this.onCancel}/>}
            {showDetails && selectedItem && <ItemDetails item={selectedItem} onEdit={this.onEditItem}  onDelete={this.onDeleteItem} />}
            {editItem && selectedItem && <EditItem onSubmit={this.onUpdateItem} onCancel={this.onCancelEdit} item={selectedItem} />}
      </div>
    );
  }
  getItems() {
    this.itemService.retrieveItems().then(items => {
          this.setState({items: items});
        }
    );
  }
  onSelect(itemLink) {
    this.clearState();
    this.itemService.getItem(itemLink).then(item => {
      this.setState({
          showDetails: true,
          selectedItem: item
        });
      }
    );
  }
  onCancel() {
    this.clearState();
  }
  onNewItem() {
    this.clearState();
    this.setState({
      newItem: true
    });
  }
  onEditItem() {
    this.setState({
      showDetails: false,
      editItem: true,
      newItem: null
    });
  }
  onCancelEdit() {
    this.setState({
      showDetails: true,
      editItem: false,
      newItem: null
    });
  }
  onUpdateItem(item) {
    this.clearState();
    this.itemService.updateItem(item).then(item => {
        this.getItems();
      }
    );
  }
  onCreateItem(newItem) {
    this.clearState();
    this.itemService.createItem(newItem).then(item => {
        this.getItems();
      }
    );
  }
  onDeleteItem(itemLink) {
    this.clearState();
    this.itemService.deleteItem(itemLink).then(res => {
        this.getItems();
      }
    );
  }
  clearState() {
    this.setState({
      showDetails: false,
      selectedItem: null,
      editItem: false,
      newItem: null
    });
  }
}
export default App;
Note that, for now, our app component uses the mock service we created in section 2: 
. . .
import ItemService from './shared/mock-item-service';
. . .
Then we will create nested components for basic operations with collection items.
Listing 3.2. src/new-item.js – creating new collection items:
import React, { Component } from 'react';
import './App.css';
import Validator from './shared/validator';
class NewItem extends Component {
  constructor(props) {
    super(props);
    this.validator = new Validator();
    this.onCancel = this.onCancel.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.state = {
      name: '',
      summary: '',
      year: '',
      country: '',
      description: ''
    };
  }
  handleInputChange(event) {
    const target = event.target;
    const value = target.value;
    const name = target.name;
    this.setState({
      [name]: value
    });
  }
  onCancel() {
    this.props.onCancel();
  }
  onSubmit() {
    if(this.validator.validateInputs(this.state)) {
        this.props.onSubmit(this.state);
    }
  }
  render() {
    return (
      <div className="input-panel">
      <span className="form-caption">New item:</span>
      <div>
        <label className="field-name">Name:<br/>
          <input value={this.state.name} name="name" maxLength="40" required onChange={this.handleInputChange} placeholder="item name" />
        </label>
      </div>
      <div>
        <label className="field-name">Summary:<br/>
          <input value={this.state.summary} name="summary" maxLength="40" required onChange={this.handleInputChange} placeholder="summary" />
        </label>
      </div>
      <div>
        <label className="field-name">Year:<br/>
          <input value={this.state.year} name="year" maxLength="4" pattern="[0-9]{1,4}" onChange={this.handleInputChange} placeholder="year" />
        </label>
      </div>
      <div>
        <label className="field-name">Country:<br/>
          <input value={this.state.country} name="country" maxLength="2" pattern="[a-z|A-Z]{2}" onChange={this.handleInputChange} placeholder="country code" />
        </label>
      </div>
      <div>
        <label className="field-name">Description:<br/>
          <textarea value={this.state.description} name="description" onChange={this.handleInputChange} placeholder="description" />
        </label>
      </div>
      <br/>
      <button onClick={() => this.onCancel()}>Cancel</button> 
      <button onClick={() => this.onSubmit()}>Create</button>
      </div>
    );
  }
}
export default NewItem;
In Listing 3.2, we use the validator class, which provides a simple validation for newly created or edited collection items. This class can be shared between components, i.e. it can be used in NewItem and EditItem components in our case.
Listing 3.3. src/shared/validatior.js – a simple validation for the item form:
class Validator {
  validateInputs(inputData) {
    let errorMsg = "";
    if(!inputData.name) {
      errorMsg +="Please enter name of this item.\n"
    }
    if(!inputData.summary) {
      errorMsg +="Please enter summary of this item.\n"
    }
    if(inputData.year.toString().match(/[^0-9]/g)) {
      errorMsg +="Year must be a number.\n"
    }
    if(inputData.country.length > 0 && !inputData.country.match(/^[a-z|A-Z][a-z|A-Z]$/)) {
      errorMsg +="Country code must be two letters.\n"
    }
    if(errorMsg.length == 0){
      return true;
    } else {
      alert(errorMsg);
      return false;
    }
  }
}
export default Validator;
Listing 3.4. src/item-details.js – viewing item details:
import React, { Component } from 'react';
import './App.css';
class ItemDetails extends Component {
  constructor(props) {
    super(props);
    this.onEdit = this.onEdit.bind(this);
    this.onDelete = this.onDelete.bind(this);
  }
  render() {
    const item = this.props.item;
    return (
      <div className="input-panel">
      <span className="form-caption">{ item.name}</span>
      <div><span className="field-name">Name:</span><br/> {item.name}</div>
      <div><span className="field-name">Summary:</span><br/> {item.summary}</div>
      <div><span className="field-name">Year:</span><br/> {item.year}</div>
      <div><span className="field-name">Country:</span><br/> {item.country}</div>
      <div><span className="field-name">Description:</span><br/> {item.description}</div>
      <br/>
      <button onClick={() => this.onDelete()}>Delete</button> 
      <button onClick={() => this.onEdit()}>Edit</button>
      </div>
    );
  }
  onEdit() {
    this.props.onEdit();
  }
  onDelete() {
    const item = this.props.item;
    if(window.confirm("Are you sure to delete item: " + item.name + " ?")) {
      this.props.onDelete(item.link);
    }
  }
}
export default ItemDetails;
Listing 3.5. src/edit-item.js – editing existing items:
import React, { Component } from 'react';
import './App.css';
import Validator from './shared/validator';
class EditItem extends Component {
  constructor(props) {
    super(props);
    this.validator = new Validator();
    this.onCancel = this.onCancel.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
    this.handleInputChange = this.handleInputChange.bind(this);
    const itemToEdit = props.item;
    this.state = {
      name: itemToEdit.name,
      summary: itemToEdit.summary,
      year: itemToEdit.year,
      country: itemToEdit.country,
      description: itemToEdit.description,
      link: itemToEdit.link
    };
  }
  handleInputChange(event) {
    const target = event.target;
    const value = target.value;
    const name = target.name;
    this.setState({
      [name]: value
    });
  }
  onCancel() {
    this.props.onCancel();
  }
  onSubmit() {
    if (this.validator.validateInputs(this.state)) {
      this.props.onSubmit(this.state);
    }
  }
  render() {
    return (
      <div className="input-panel">
      <span className="form-caption">Edit item:</span> <span>{this.state.name}</span>
      <div>
        <label className="field-name">Name:<br/>
          <input value={this.state.name} name="name" maxLength="40" required onChange={this.handleInputChange} placeholder="item name" />
        </label>
      </div>
      <div>
        <label className="field-name">Summary:<br/>
          <input value={this.state.summary} name="summary" maxLength="40" required onChange={this.handleInputChange} placeholder="summary" />
        </label>
      </div>
      <div>
        <label className="field-name">Year:<br/>
          <input value={this.state.year} name="year" maxLength="4" pattern="[0-9]{1,4}" onChange={this.handleInputChange} placeholder="year" />
        </label>
      </div>
      <div>
        <label className="field-name">Country:<br/>
          <input value={this.state.country} name="country" maxLength="2" pattern="[a-z|A-Z]{2}" onChange={this.handleInputChange} placeholder="country" />
        </label>
      </div>
      <div>
        <label className="field-name">Description:<br/>
          <textarea value={this.state.description} name="description" onChange={this.handleInputChange} placeholder="description" />
        </label>
      </div>
      <br/>
      <button onClick={() => this.onCancel()}>Cancel</button> 
      <button onClick={() => this.onSubmit()}>Update</button>
      </div>
    );
  }
}
export default EditItem;
Here we use the lifting-state-up approach. Instead of maintaining state in each child component and synchronizing the states, and hence the appearance of related components, we lift the shared state up to their closest common ancestor. So, we maintain state in the parent app component with the usage of callback functions which are passed to child components via properties. Then we call the callback functions inside event handlers in the child components. In these functions, we change the parent component state correspondingly to the user actions triggered in the child components. Based on the parent component state change, React rerenders child components, if appropriate. For example, see how theApp.onEditItem() method is called in theItemDetails.onEdit() event handler, which is triggered when the user clicks the Edit button.
This way, we have one-point state management that makes our UI model more consistent.
Note: Redux technology provides an even more consistent and effective way of managing component model state, especially in large applications.
Provided we have all the scripts in place, we can see the main application at http://localhost:3000:
React.js applicationBy clicking on an item in the list, we can see the item details:
React.js application
If we need to edit an item, we can make the detail view editable with the Edit button:
React.js application
Also, we can add new items with the New Item button:
React.js application
To have our UI really functional, we need to make it exchange data with the backend.

4. Real Communication

While React doesn't provide any built-in support for sending requests to the server, we are free to use any communication library inside our React applications. Let's use Fetch API, which is becoming a standard way to send HTTP requests and is supported in most modern browsers. Provided we have our communication interface defined, we can easily substitute our mock service implementation (see section 2) with a fully functional version, like the following.
Listing 4.1. src/shared/item-service,js – real functional version of ItemService:
import Configuration from './configuration';
class ItemService {
  constructor() {
    this.config = new Configuration();
  }
  async retrieveItems() {
    return fetch(this.config.ITEM_COLLECTION_URL)
      .then(response => {
        if (!response.ok) {
          this.handleResponseError(response);
        }
        return response.json();
      })
      .then(json => {
        console.log("Retrieved items:");
        console.log(json);
        const items = [];
        const itemArray = json._embedded.collectionItems;
        for(var i = 0; i < itemArray.length; i++) {
          itemArray[i]["link"] =  itemArray[i]._links.self.href;
          items.push(itemArray[i]);
        }
        return items;
      })
      .catch(error => {
        this.handleError(error);
      });
  }
  async getItem(itemLink) {
    console.log("ItemService.getItem():");
    console.log("Item: " + itemLink);
    return fetch(itemLink)
      .then(response => {
        if (!response.ok) {
            this.handleResponseError(response);
        }
        return response.json();
      })
      .then(item => {
          item["link"] = item._links.self.href;
          return item;
        }
      )
      .catch(error => {
        this.handleError(error);
      });
  }
  async createItem(newitem) {
    console.log("ItemService.createItem():");
    console.log(newitem);
    return fetch(this.config.ITEM_COLLECTION_URL, {
      method: "POST",
      mode: "cors",
      headers: {
            "Content-Type": "application/json"
        },
      body: JSON.stringify(newitem)
    })
      .then(response => {
       if (!response.ok) {
            this.handleResponseError(response);
        }
        return response.json();
      })
      .catch(error => {
        this.handleError(error);
      });
  }
  async deleteItem(itemlink) {
    console.log("ItemService.deleteItem():");
    console.log("item: " + itemlink);
    return fetch(itemlink, {
      method: "DELETE",
      mode: "cors"
    })
      .then(response => {
        if (!response.ok) {
            this.handleResponseError(response);
        }
      })
      .catch(error => {
        this.handleError(error);
      });
  }
  async updateItem(item) {
    console.log("ItemService.updateItem():");
    console.log(item);
    return fetch(item.link, {
      method: "PUT",
      mode: "cors",
      headers: {
            "Content-Type": "application/json"
          },
      body: JSON.stringify(item)
    })
      .then(response => {
        if (!response.ok) {
          this.handleResponseError(response);
        }
        return response.json();
      })
      .catch(error => {
        this.handleError(error);
      });
  }
  handleResponseError(response) {
      throw new Error("HTTP error, status = " + response.status);
  }
  handleError(error) {
      console.log(error.message);
  }
}
export default ItemService;
Here we also follow the single-responsibility principle and put all configuration settings into one object, Configuration, which can be imported into all relevant components.
Now we have all the basic modules developed and can put all the things together and run our application.

5. Running the Front-End Application

Provided we have our backend running on http://localhost:8080, we can set its URL in the configuration class.
Listing 5.1. Configuration class – one-point application configuration:
class Configuration {
  ITEM_COLLECTION_URL = "http://localhost:8080/collectionItems";
}
export default Configuration;
And start up our application:
.../project-root/consuming-rest>npm start
This time, we see the main application screen with real data from the backend:
React.js application
We can add new items, like the following screenshot illustrates:
React.js application
New item added:
React.js application
So, we have developed a fully functional web application, which supports main collection management operations, i.e. the ability to add, view, update, and delete items. Using the React component model, we can create sophisticated UIs with nested, multi-page views, providing a rich user experience. More details can be found at the React.js official site and sites for related technologies, like:
  • Redux - state management library.
  • Formik - HTML form support library.
  • Jest - Testing React applications.

Monday, April 8, 2019

Top 5 Java Test Frameworks for Automation in 2019

Source: https://dzone.com/articles/top-5-java-test-frameworks-for-automation-in-2019

Check out this post to learn more about the top Java test frameworks for automation testing.


For decades, Java has been the most preferred programming language for developing the server side layer of an application. Although JUnit has been there with the developers for helping them in automated unit testing, with time and the evolution of testing, when automation testing is currently on the rise, many open-source frameworks have been developed that are based on Java and varying a lot from JUnit in terms of validation and business logic. Here, I will be talking about the top five Java test frameworks of 2019 for performing test automation with Selenium WebDriver and Java. I will also highlight what is unique about these top Java test frameworks.

JUnit

Kent Beck and Erich Gamma developer JUnit, which is an instance of xUnit. The main purpose behind it was to enable Java developers for scripting and executing repeatable test cases. It is usually used to test small chunks of code. You can also perform automation testing of a website by integrating JUnit with Selenium WebDriver for Java test automation. Whenever any new piece of code is added, you will be required to re-execute the entire test cases and make sure that there is no breakage.

What Are the Prerequisites?

The framework is highly compatible with Selenium WebDriver for Java, with the reason being that JUnit is a framework based on Java. As some prerequisites, you will need to:
  • Have the latest version of JDK installed in your workstation.
  • Download the latest version of JUnit and set up the environment.
  • Have a good knowledge of application development in an object-oriented programming language, especially Java.

Advantages and Disadvantages of Using JUnit?

There are several advantages to JUnit.
  • Developers who work in an environment that is test driven find it highly beneficial since they are forced to read the code and find if there is an anomaly.
  • Bugs are detected early, resulting in a more reliable code.
  • Development of a more readable and error-free code acts as a confidence booster.
  • With the latest version of JUnit (version 4), exceptions are identified easily. You can even execute test cases written in the old versions of JUnit.
  • You can also use it with Java 5 or versions above.
  • The framework is unable to execute dependency tests. That is where we need TestNG.
The only disadvantage of JUnit is:
  • The framework is unable to execute dependency tests. That is where we need TestNG.

Is JUnit the Top Java Test Framework for You?

Both JUnit, as well as TestNG, perform the same job. And their features are almost the same, except dependency tests are not performed using JUnit and the implementation procedure of parameterized test is different in both the frameworks. Also, since JUnit is being used for a long time, there is better community support and it has been defined as the standard for unit testing using Selenium WebDriver for Java-based applications. Although TestNG has few users, the community is pretty huge and growing every day. So, we can conclude that the selection between TestNG or JUnit for a Java test framework depends entirely upon the application’s nature and requirement.
If you wish to get started with JUnit as your Java test framework, here is a step-by-step process of performing automated testing with JUnit and Selenium for browser compatibility.

JBehave

We all know about Behavior Driven Development or BDD. It is the type of testing that describes acceptance testing in a transparent manner to the business users. JBehave is another Java test framework used for BDD testing, mostly used with Selenium WebDriver for Java. The primary objective behind the introduction of JBehave is to make it possible for newcomers to easily understand and get familiar with BDD. It is kind of a design philosophy that makes the testing phase of an application more based on its behavior.

What Are the Prerequisites?

The ideal way to use JBehave is by integrating with Eclipse. For that, apart from JDK1.7 and above and any Eclipse IDE above Indigo, you will need several jar files like:
  • Junit-4.1.0.jar
  • Jbehave-core-3.8.jar
  • Commons-lang-2.4.jar
  • Paranamer-2.5.jar
  • Freemarker-2.3.9.jar
  • Org.apacje.commons.io.jar
  • Org.apache.commons.collections.jar
  • Plexus-utils-1.1.jar

Advantages and Disadvantages of JBehave

Like all other BDD testing frameworks, JBehave is also advantageous in many ways.
  • Serves the most important purpose of behavior driven development by enabling better coordination between different development teams who are working on different projects with similar specs.
  • Project managers and stakeholders get a better clarity regarding the Dev team and QA team’s output since the specs have a similar format.
  • Products have better specifications since JBehave features reasoning and thinking in details.
  • JBehave uses a semi-formal language and it also has a domain vocabulary which helps to maintain a consistent behavior in the team structure.
Well, there is only one disadvantage of JBehave just like any other BDD testing tool.
  • The success of a BDD testing tool depends mostly on the communication carried out between different members involved in a project, the stakeholders, the developers, the testers as well as the management of the organization. Lack of communication may lead to unanswered questions and doubts which may ultimately lead to a buggy application or an application not satisfying the business requirement, resulting in all the parties blaming each other.

Is JBehave The Top Java Test Framework For You?

JBehave works the same way as Serenity. However, if you are aiming to make your automated acceptance tests more efficient, it is ideal to integrate Serenity along with JBehave for utilizing a better testing experience. This is because Serenity’s core concept is based on BDD development and it also enables the users to write a powerful and rich test report. We’ll discuss more on it in the following section.

Serenity

Serenity is an open-source library meant for behavior-driven testing. The framework helps you to write acceptance criteria, which are well-structured and more maintained. It extends the functionality of JUnit and WebDriver. Initially, it was known as Thucydides. The reason behind its name change was nothing but the difficulty in pronunciation. Still, the old name can be used while referring.

What Are the Prerequisites for Installing Serenity?

Since the framework is Java-based, you will obviously need knowledge and experience in Java or any other object-oriented programming language. Apart from that, in your workstation, you will need:
  • JDK 5 or higher version.
  • Maven 3.0 or any version above.
  • An IDE tool – Eclipse is usually preferred by all developers since a combination of Maven and Eclipse is easier to work with.

Advantages and Disadvantages of Serenity

Let’s take a look at the advantages of Serenity brought into the world of behavior driven development.
  • The framework helps the tester in creating test cases for REST services.
  • Users do not have to spend a lot of time in maintaining or building their own automation framework.
  • It keeps a balance, rather a boundary between the definition and understanding of a test case and its way of implementation.
  • It can be integrated with lots of automation frameworks like Selenium, JBehave, advanced Java-based development frameworks like Spring and even with continuous integration tools like JIRA.
The only disadvantage of Serenity is just like JBehave. Constant communication between the project participants is required in order to utilize its complete feature of supporting a behavior driven development.

Is Serenity the Best Java Test Framework for You?

The tool is mainly used for reporting acceptance criteria. However, the reports written using Serenity are much informative and rich than JBehave or any other BDD framework. It also helps developers to write automation test case scenarios of better quality and also provides support for RestAssured and Selenium WebDriver, making it faster and simpler for the tester to write automation based acceptance test criteria.

TestNG

Cedric Beust created TestNG, which is an open-source, Java-based automation testing framework inspired by JUnit. However, it is much more powerful. The NG means Next Generation in TestNG. The framework is designed to perform in a better manner during integration. The framework helps developers with features like sequencing, grouping, and parameterizing, thereby making the new test cases more flexible and eliminating the drawbacks of frameworks used earlier.

Prerequisites for Installing TestNG

For installing TestNG, you will need:
  • The latest version of JDK installed in your system.
  • The latest version of Eclipse
  • Knowledge and hands-on experience of Java or any other object-oriented programming language

Advantages and Disadvantages of TestNG

Apart from overcoming the drawbacks of older frameworks, there are several advantages of TestNG.
  • The framework enables you to run parallel tests on multiple code snippets.
  • During the test case executing, you can generate an HTML report.
  • Test cases can be grouped and arranged according to priorities. Executing the tests are much simpler just by asking the framework to run database tests or front-end tests or whatever you want.
  • You can parameterize the data and use annotations to easily set the priorities.
The only disadvantage is:
  • It depends on your requirement. You may not want to use TestNG if your project does not require test case prioritization. Besides, setting up TestNG requires a bit of time.

Is TestNG the Best Java Test Framework for You?

Although JUnit performs all the same functionalities as TestNG, it has certainly has its limitations. JUnit is perfect when testing is done in isolation. But when there are dependencies; you will not have any control over which test is executed first. TestNG helps you by allowing you to run the test cases in any order you want. Besides, it also helps in parameterized testing. Although this feature is already present in JUnit 4.5, TestNG is much more effective. If your project is complicated and you may need more than a hundred test cases, it is ideal to take some time and set up TestNG instead of depending on JUnit.
If you are new to TestNG, then here is a complete guide for running your first TestNG automation script for performing automated cross-browser testing using Selenium WebDriver with Java.

Selenide

Powered by Selenium, Selenide is a popular tool for writing stable, precise, and expressive UI test cases for a Java application. Testing of modern web technologies like Ajax has certain complications like a timeout. WebDriver is the popular tool for UI tests, yet it lacked the feature to deal with a timeout. Selenide deals with all these problems in a simple manner. Also, it’s easier to grab and learn. You don’t need any requirement for Google and search for tutorials. All you need to do is focus on the business logic, and in just a few simple lines of codes, your job will be done.

What Are the Prerequisites for Using Selenide?

Installation of Selenide is as simple as it can be. If you are using Maven, you will need to add the following lines in your pom.xml file.
<dependency>     
<groupId>com.codeborne</groupId>     
<artifactId>selenide</artifactId>     
<version>5.1.0</version>     
<scope>test</scope> 
</dependency>

If you are using Ivy in the ivy.xml file, you will need to add:
<ivy-module>   
<dependencies>     
<dependency org="com.codeborne" name="selenide" rev="5.1.0"/>   
</dependencies> 
</ivy-module>

And just like that, the framework is ready for you to start testing.

Advantages and Disadvantages of Selenide?

In the front-end layer of an application, where Java is used on the server side, the most common problem faced by a tester is timeouts. The test cases which you wrote may work fine at present, but after a few days, maybe some Ajax request will take up some more time than it does now or some JavaScript will run slower. Also, your system may run another process at the same time, resulting in the failure of your test case. What’s more unfortunate, you may spend days finding out the source of those problems. Selenide helps you by:
  • Concise writing procedure of test cases that eliminates the timeout problems.
  • Supports testing of applications developed using AngularJS
  • Have reduced most of the commands which were used by traditional Selenium tools.
Until now, we have not found any notable disadvantage of using Selenide. Let us know if you have found any limitations that may impact your work.

Is Selenide the Best Java Test Framework for You?

For UI testing, there is no better Java-based framework apart from Selenium WebDriver, the parent framework of Selenide. Obviously, WebDriver cannot solve problems caused by Ajax Timeout or slow running JavaScript or any Dynamic Content that takes time to load. To overcome problems, we previously used “ wait_until” or “ sleep” methods in our test cases. With Selenide, we don’t need to think about those problems anymore. Just focus on the business logic and the test cases will work fine in serving their purpose.
If you plan to opt Selenide as your Java test framework for automation testing, then here is a guide to help you run Selenium automation tests using Selenide, IntelliJ, and Maven.
In the current Agile age, developers are also required to get involved in testing. Knowledge and knowing the importance of testing is what separates professional Java developers from amateurs. It is not necessary that you should be an expert, but at least you should have the basic knowledge to write test cases that can automatically validate your code. In this article, we have gone through five tools which are preferred by developers worldwide for UI testing, unit testing, and BDD testing of an application. Based on the comparison, we can conclude that if you are sincere in your job and want to become an expert developer as well as a tester, the tools mentioned above are something on which you will find useful in your daily assignments.