Categories
Uncategorized

A Step-by-Step Tutorial for Your First AngularJS App

What is AngularJS?

AngularJS is a JavaScript MVC framework developed by Google that lets you build well structured, easily testable, and maintainable front-end applications.

And Why Should I Use It?

If you haven’t tried AngularJS yet, you’re missing out. The framework consists of a tightly integrated toolset that will help you build well structured, rich client-side applications in a modular fashion—with less code and more flexibility.

AngularJS extends HTML by providing directives that add functionality to your markup and allow you to create powerful dynamic templates. You can also create your own directives, crafting reusable components that fill your needs and abstracting away all the DOM manipulation logic.

It also implements two-way data binding, connecting your HTML (views) to your JavaScript objects (models) seamlessly. In simple terms, this means that any update on your model will be immediately reflected in your view without the need for any DOM manipulation or event handling (e.g., with jQuery).

Angular provides services on top of XHR that dramatically simplify your code and allow you to abstract API calls into reusable services. With that, you can move your model and business logic to the front-end and build back-end agnostic web apps.

Finally, I love Angular because of its flexibility regarding server communication. Like most JavaScript MVC frameworks, it lets you work with any server-side technology as long as it can serve your app through a RESTful web API. But Angular also provides services on top of XHR that dramatically simplify your code and allow you to abstract API calls into reusable services. As a result, you can move your model and business logic to the front-end and build back-end agnostic web apps. In this post, we’ll do just that, one step at a time.

So, Where Do I Begin?

First, let’s decide the nature of the app we want to build. In this guide, we’d prefer not to spend too much time on the back-end, so we’ll write something based on data that’s easily attainable on the Internet—like a sports feed app!

Since I happen to be a huge fan of motor racing and Formula 1, I’ll use an autosport API service to act as our back-end. Luckily, the guys at Ergast are kind enough to provide a free motorsport API that will be perfect for us.

For a sneak peak at what we’re going to build, take a look at the live demo. To prettify the demo and show off some Angular templating, I applied a Bootstrap theme from WrapBootstrap, but seeing as this article isn’t about CSS, I’ll just abstract it away from the examples and leave it out.

Getting Started Tutorial

Let’s kickstart our example app with some boilerplate. I recommend the angular-seed project as it not only provides you with a great skeleton for bootstrapping, but also sets the ground for unit testing with Karma and Jasmine (we won’t be doing any testing in this demo, so we’ll just leave that stuff aside for now; see Part 2 of this tutorial for more info on setting up your project for unit and end-to-end testing).

EDIT (May 2014): Since I wrote this tutorial, the angular-seed project has gone through some heavy changes (including the additon of Bower as package manager). If you have any doubts about how to deploy the project, take a quick look at the first section of their reference guide. In Part 2 of ths tutorial, Bower, among other tools, is covered in greater detail.

OK, now that we’ve cloned the repository and installed the dependencies, our app’s skeleton will look like this:

angularjs tutorial - start with the skeleton

Now we can start coding. As we’re trying to build a sports feed for a racing championship, let’s begin with the most relevant view: the championship table.

the championship table

Given that we already have a drivers list defined within our scope (hang with me – we’ll get there), and ignoring any CSS (for readability), our HTML might look like:

<body ng-app="F1FeederApp" ng-controller="driversController">
  <table>
    <thead>
      <tr><th colspan="4">Drivers Championship Standings</th></tr>
    </thead>
    <tbody>
      <tr ng-repeat="driver in driversList">
        <td>{{$index + 1}}</td>
        <td>
          <img src="img/flags/{{driver.Driver.nationality}}.png" />
          {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
        </td>
        <td>{{driver.Constructors[0].name}}</td>
        <td>{{driver.points}}</td>
      </tr>
    </tbody>
  </table>
</body>

The first thing you’ll notice in this template is the use of expressions (“{{“ and “}}”) to return variable values. In AngularJS, expressions allow you to execute some computation in order to return a desired value. Some valid expressions would be:

  • {{ 1 + 1 }}
  • {{ 946757880 | date }}
  • {{ user.name }}

Effectively, expressions are JavaScript-like snippets. But despite being very powerful, you shouldn’t use expressions to implement any higher-level logic. For that, we use directives.

Understanding Basic Directives

The second thing you’ll notice is the presence of ng-attributes, which you wouldn’t see in typical markup. Those are directives.

At a high level, directives are markers (such as attributes, tags, and class names) that tell AngularJS to attach a given behaviour to a DOM element (or transform it, replace it, etc.). Let’s take a look at the ones we’ve seen already:

  • The ng-app directive is responsible for bootstrapping your app defining its scope. In AngularJS, you can have multiple apps within the same page, so this directive defines where each distinct app starts and ends.
  • The ng-controller directive defines which controller will be in charge of your view. In this case, we denote the driversController, which will provide our list of drivers (driversList).
  • The ng-repeat directive is one of the most commonly used and serves to define your template scope when looping through collections. In the example above, it replicates a line in the table for each driver in driversList.

Adding Controllers

Of course, there’s no use for our view without a controller. Let’s add driversController to our controllers.js:

angular.module('F1FeederApp.controllers', []).
controller('driversController', function($scope) {
    $scope.driversList = [
      {
          Driver: {
              givenName: 'Sebastian',
              familyName: 'Vettel'
          },
          points: 322,
          nationality: "German",
          Constructors: [
              {name: "Red Bull"}
          ]
      },
      {
          Driver: {
          givenName: 'Fernando',
              familyName: 'Alonso'
          },
          points: 207,
          nationality: "Spanish",
          Constructors: [
              {name: "Ferrari"}
          ]
      }
    ];
});

You may have noticed the $scope variable we’re passing as a parameter to the controller. The $scope variable is supposed to link your controller and views. In particular, it holds all the data that will be used within your template. Anything you add to it (like the driversList in the above example) will be directly accessible in your views. For now, let’s just work with a dummy (static) data array, which we will replace later with our API service.

Now, add this to app.js:

angular.module('F1FeederApp', [
  'F1FeederApp.controllers'
]);

With this line of code, we actually initialize our app and register the modules on which it depends. We’ll come back to that file (app.js) later on.

Now, let’s put everything together in index.html:

<!DOCTYPE html>
<html>
<head>
  <title>F-1 Feeder</title>
</head>

<body ng-app="F1FeederApp" ng-controller="driversController">
  <table>
    <thead>
      <tr><th colspan="4">Drivers Championship Standings</th></tr>
    </thead>
    <tbody>
      <tr ng-repeat="driver in driversList">
        <td>{{$index + 1}}</td>
        <td>
          <img src="img/flags/{{driver.Driver.nationality}}.png" />
          {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
        </td>
        <td>{{driver.Constructors[0].name}}</td>
        <td>{{driver.points}}</td>
      </tr>
    </tbody>
  </table>
  <script src="bower_components/angular/angular.js"></script>
  <script src="bower_components/angular-route/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="js/services.js"></script>
  <script src="js/controllers.js"></script>
</body>
</html>

Modulo minor mistakes, you can now boot up your app and check your (static) list of drivers.

Note: If you need help debugging your app and visualizing your models and scope within the browser, I recommend taking a look at the awesome Batarang plugin for Chrome.

 

Loading Data From the Server

Since we already know how to display our controller’s data in our view, it’s time to actually fetch live data from a RESTful server.

To facilitate communication with HTTP servers, AngularJS provides the $http and $resource services. The former is but a layer on top of XMLHttpRequest or JSONP, while the latter provides a higher level of abstraction. We’ll use $http.

To abstract our server API calls from the controller, let’s create our own custom service which will fetch our data and act as a wrapper around $http by adding this to our services.js:

angular.module('F1FeederApp.services', []).
  factory('ergastAPIservice', function($http) {

    var ergastAPI = {};

    ergastAPI.getDrivers = function() {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    return ergastAPI;
  });

With the first two lines, we create a new module (F1FeederApp.services) and register a service within that module (ergastAPIservice). Notice that we pass $http as a parameter to that service. This tells Angular’s dependency injection engine that our new service requires (or depends on) the $http service.

In a similar fashion, we need to tell Angular to include our new module into our app. Let’s register it with app.js, replacing our existing code with:

angular.module('F1FeederApp', [
  'F1FeederApp.controllers',
  'F1FeederApp.services'
]);

Now, all we need to do is tweak our controller.js a bit, include ergastAPIservice as a dependency, and we’ll be good to go:

angular.module('F1FeederApp.controllers', []).
  controller('driversController', function($scope, ergastAPIservice) {
    $scope.nameFilter = null;
    $scope.driversList = [];

    ergastAPIservice.getDrivers().success(function (response) {
        //Dig into the responde to get the relevant data
        $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;
    });
  });

Now reload the app and check out the result. Notice that we didn’t make any changes to our template, but we added a nameFilter variable to our scope. Let’s put that variable to use.

Filters

Great! We have a functional controller. But it only shows a list of drivers. Let’s add some functionality by implementing a simple text search input which will filter our list. Let’s add the following line to our index.html, right below the <body> tag:

<input type="text" ng-model="nameFilter" placeholder="Search..."/>

We are now making use of the ng-model directive. This directive binds our text field to the $scope.nameFilter variable and makes sure that its value is always up-to-date with the input value. Now, let’s visit index.html one more time and make a small adjustment to the line that contains the ng-repeat directive:

<tr ng-repeat="driver in driversList | filter: nameFilter">

This line tells ng-repeat that, before outputting the data, the driversList array must be filtered by the value stored in nameFilter.

At this point, two-way data binding kicks in: every time a value is input in the search field, Angular immediately ensures that the $scope.nameFilter that we associated with it is updated with the new value. Since the binding works both ways, the moment the nameFilter value is updated, the second directive associated to it (i.e., the ng-repeat) also gets the new value and the view is updated immediately.

Reload the app and check out the search bar.

app search bar

Notice that this filter will look for the keyword on all attributes of the model, including the ones we´re not using. Let’s say we only want to filter by Driver.givenName and Driver.familyName: First, we add to driversController, right below the $scope.driversList = []; line:

$scope.searchFilter = function (driver) {
    var keyword = new RegExp($scope.nameFilter, 'i');
    return !$scope.nameFilter || keyword.test(driver.Driver.givenName) || keyword.test(driver.Driver.familyName);
};

Now, back to index.html, we update the line that contains the ng-repeat directive:

<tr ng-repeat="driver in driversList | filter: searchFilter">

Reload the app one more time and now we have a search by name.

Routes

Our next goal is to create a driver details page which will let us click on each driver and see his/her career details.

First, let’s include the $routeProvider service (in app.js) which will help us deal with these varied application routes. Then, we’ll add two such routes: one for the championship table and another for the driver details. Here’s our new app.js:

angular.module('F1FeederApp', [
  'F1FeederApp.services',
  'F1FeederApp.controllers',
  'ngRoute'
]).
config(['$routeProvider', function($routeProvider) {
  $routeProvider.
	when("/drivers", {templateUrl: "partials/drivers.html", controller: "driversController"}).
	when("/drivers/:id", {templateUrl: "partials/driver.html", controller: "driverController"}).
	otherwise({redirectTo: '/drivers'});
}]);

With that change, navigating to http://domain/#/drivers will load the driversController and look for the partial view to render in partials/drivers.html. But wait! We don’t have any partial views yet, right? We’ll need to create those too.

Partial Views

AngularJS will allow you to bind your routes to specific controllers and views.

But first, we need to tell Angular where to render these partial views. For that, we’ll use the ng-view directive, modifying our index.html to mirror the following:

<!DOCTYPE html>
<html>
<head>
  <title>F-1 Feeder</title>
</head>

<body ng-app="F1FeederApp">
  <ng-view></ng-view>
  <script src="bower_components/angular/angular.js"></script>
  <script src="bower_components/angular-route/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="js/services.js"></script>
  <script src="js/controllers.js"></script>
</body>
</html>

Now, whenever we navigate through our app routes, Angular will load the associated view and render it in place of the <ng-view> tag. All we need to do is create a file named partials/drivers.html and put our championship table HTML there. We’ll also use this chance to link the driver name to our driver details route:

<input type="text" ng-model="nameFilter" placeholder="Search..."/>
<table>
<thead>
  <tr><th colspan="4">Drivers Championship Standings</th></tr>
</thead>
<tbody>
  <tr ng-repeat="driver in driversList | filter: searchFilter">
    <td>{{$index + 1}}</td>
    <td>
      <img src="img/flags/{{driver.Driver.nationality}}.png" />
      <a href="#/drivers/{{driver.Driver.driverId}}">
	  	{{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
	  </a>
	</td>
    <td>{{driver.Constructors[0].name}}</td>
    <td>{{driver.points}}</td>
  </tr>
</tbody>
</table>

Finally, let’s decide what we want to show in the details page. How about a summary of all the relevant facts about the driver (e.g., birth, nationality) along with a table containing his/her recent results? To do that, we add to services.js:

angular.module('F1FeederApp.services', [])
  .factory('ergastAPIservice', function($http) {

    var ergastAPI = {};

    ergastAPI.getDrivers = function() {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    ergastAPI.getDriverDetails = function(id) {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    ergastAPI.getDriverRaces = function(id) {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/results.json?callback=JSON_CALLBACK'
      });
    }

    return ergastAPI;
  });

This time, we provide the driver’s ID to the service so that we retrieve the information relevant solely to a specific driver. Now, we modify controllers.js:

angular.module('F1FeederApp.controllers', []).

  /* Drivers controller */
  controller('driversController', function($scope, ergastAPIservice) {
    $scope.nameFilter = null;
    $scope.driversList = [];
    $scope.searchFilter = function (driver) {
        var re = new RegExp($scope.nameFilter, 'i');
        return !$scope.nameFilter || re.test(driver.Driver.givenName) || re.test(driver.Driver.familyName);
    };

    ergastAPIservice.getDrivers().success(function (response) {
        //Digging into the response to get the relevant data
        $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;
    });
  }).

  /* Driver controller */
  controller('driverController', function($scope, $routeParams, ergastAPIservice) {
    $scope.id = $routeParams.id;
    $scope.races = [];
    $scope.driver = null;

    ergastAPIservice.getDriverDetails($scope.id).success(function (response) {
        $scope.driver = response.MRData.StandingsTable.StandingsLists[0].DriverStandings[0]; 
    });

    ergastAPIservice.getDriverRaces($scope.id).success(function (response) {
        $scope.races = response.MRData.RaceTable.Races; 
    }); 
  });

The important thing to notice here is that we just injected the $routeParams service into the driver controller. This service will allow us to access our URL parameters (for the :id, in this case) using $routeParams.id.

Now that we have our data in the scope, we only need the remaining partial view. Let’s create a file named partials/driver.html and add:

<section id="main">
  <a href="./#/drivers"><- Back to drivers list</a>
  <nav id="secondary" class="main-nav">
    <div class="driver-picture">
      <div class="avatar">
        <img ng-show="driver" src="img/drivers/{{driver.Driver.driverId}}.png" />
        <img ng-show="driver" src="img/flags/{{driver.Driver.nationality}}.png" /><br/>
        {{driver.Driver.givenName}} {{driver.Driver.familyName}}
      </div>
    </div>
    <div class="driver-status">
      Country: {{driver.Driver.nationality}}   <br/>
      Team: {{driver.Constructors[0].name}}<br/>
      Birth: {{driver.Driver.dateOfBirth}}<br/>
      <a href="{{driver.Driver.url}}" target="_blank">Biography</a>
    </div>
  </nav>

  <div class="main-content">
    <table class="result-table">
      <thead>
        <tr><th colspan="5">Formula 1 2013 Results</th></tr>
      </thead>
      <tbody>
        <tr>
          <td>Round</td> <td>Grand Prix</td> <td>Team</td> <td>Grid</td> <td>Race</td>
        </tr>
        <tr ng-repeat="race in races">
          <td>{{race.round}}</td>
          <td><img  src="img/flags/{{race.Circuit.Location.country}}.png" />{{race.raceName}}</td>
          <td>{{race.Results[0].Constructor.name}}</td>
          <td>{{race.Results[0].grid}}</td>
          <td>{{race.Results[0].position}}</td>
        </tr>
      </tbody>
    </table>
  </div>

</section>

Notice that we’re now putting the ng-show directive to good use. This directive will only show the HTML element if the expression provided is true (i.e., neither false, nor null). In this case, the avatar will only show up once the driver object has been loaded into the scope by the controller.

Finishing Touches

Add in a bunch of CSS and render your page. You should end up with something like this:

page rendered with CSS

You’re now ready to fire up your app and make sure both routes are working as desired. You could also add a static menu to index.html to improve the user’s navigation capabilities. The possibilities are endless.

EDIT (May 2014): I’ve received many requests for a downloadable version of the code that we build in this tutorial. I’ve therefore decided to release it here (stripped of any CSS). However, I really do not recommend downloading it, since this guide contains every single step you need to build the same application with your own hands, which will be a much more useful and effective learning exercise.

Conclusion

At this point in the tutorial, we’ve covered everything you’d need to write a simple app (like a Formula 1 feeder). Each of the remaining pages in the live demo (e.g., constructor championship table, team details, calendar) share the same basic structure and concepts that we’ve reviewed here.

Finally, keep in mind that Angular is a very powerful framework and we’ve barely scratched the surface in terms of everything it has to offer. In Part 2 of this tutorial, we’ll give examples of why Angular stands out among its peer front-end MVC frameworks: testability. We’ll review the process of writing and running unit tests with Karma, achieving continuous integration with Yeomen, Grunt, and Bower, and other strengths of this fantastic front-end framework.

Source: Toptal

Categories
Uncategorized

Test Automation in Selenium Using Page Object Model and Page Factory

Writing automated tests is more than just a luxury for any agile software development team. It is a need, and is an essential tool to find bugs quickly during early phases of software development cycles. When there is a new feature that is still in development phase, developers can run automated tests and see how other parts of the system are affected by those changes.

Through test automation, it is possible to lower the cost of bug fixing and bring overall improvement to quality assurance (QA) process. With proper tests, developers get a chance at finding and resolving bugs even before it gets to QA. Test automation further helps us to automate test cases and features that are constantly regressing. This way QAs have more time in testing other parts of the application. Moreover, this helps in ensuring quality of the product in production releases. As a result, we get products that are effectively more stable, and a QA process that is more efficient.

Selenium simplifies test automation for web applications

Selenium simplifies test automation for web applications

Although writing automated tests may seem like an easy task for developers and engineers, there is still the possibility of ending up with poorly implemented tests, and the high cost of code maintenance in any agile process. Trying to constantly deliver changes or features in any agile development project can prove to be costly when tests are involved. Changing one element on a web page that 20 tests rely on will require one to go through these 20 test routines and update each one to adapt to this newly introduced change. Not only can this be really time consuming, but a serious de-motivating factor when it comes to implementing automated tests early on.

But, what if we could make the change in one place only, and have every relevant test routine use it? In this article, we will take a look at automated tests in Selenium, and how we can use Page Object models to write maintainable and reusable test routines.

Page Object Model in Selenium

Page Object model is an object design pattern in Selenium, where web pages are represented as classes, and the various elements on the page are defined as variables on the class. All possible user interactions can then be implemented as methods on the class:

clickLoginButton();
setCredentials(user_name,user_password);

Since well-named methods in classes are easy to read, this works as an elegant way to implement test routines that are both readable and easier to maintain or update in the future. For example:

In order to support Page Object model, we use Page Factory. Page Factory is an extension to Page Object and can be used in various ways. In this case we will use Page Factory to initialize web elements that are defined in web page classes or Page Objects.

Web page classes or Page Objects containing web elements need to be initialized using Page Factory before the web element variables can be used. This can be done simply through the use of initElements function on PageFactory:

LoginPage page = new LoginPage(driver);
PageFactory.initElements(driver, page);

Or, even simpler:

LoginPage page = PageFactory.intElements(driver,LoginPage.class)

Or, inside the web page class constructor:

public LoginPage(WebDriver driver) {           
         this.driver = driver; 
         PageFactory.initElements(driver, this);
}

Page Factory will initialize every WebElement variable with a reference to a corresponding element on the actual web page based on configured “locators”. This is done through the use of @FindBy annotations. With this annotation, we can define a strategy for looking up the element, along with the necessary information for identifying it:

@FindBy(how=How.NAME, using="username")
private WebElement user_name;

Every time a method is called on this WebElement variable, the driver will first find it on the current page and then simulate the interaction. In case we are working with a simple page, we know that we will find the element on the page every time we look for it, and we also know that we will eventually navigate away from this page and not return to it, we can cache the looked up field by using another simple annotation:

@FindBy(how=How.NAME, using="username")
@CacheLookup
private WebElement user_name;

This entire definition of the WebElement variable can be replaced with its much more concise form:

@FindBy(name="username")
private WebElement user_name;

The @FindBy annotation supports a handful of other strategies that make things a bit easier:

id, name, className, css, tagName, linkText, partialLinkText, xpath
@FindBy(id="username")
private WebElement user_name;


@FindBy(name="passsword")
private WebElement user_password;


@FindBy(className="h3")
 private WebElement label;


@FindBy(css=”#content”)
private WebElement text;

Once initialized, these WebElement variables can then be used to interact with the corresponding elements on the page. The following code will, for example:

user_password.sendKeys(password);

… send the given sequence of keystrokes to the password field on the page, and it is equivalent to:

driver.findElement(By.name(“user_password”)).sendKeys(password);

Moving on, you will often come across situations where you need to find a list of elements on a page, and that is when @FindBys comes in handy:

@FindBys(@FindBy(css=”div[class=’yt-lockup-tile yt-lockup-video’]”)))
private List<WebElement> videoElements;

The above code will find all the div elements having two class names “yt-lockup-tile” and “yt-lockup-video”. We can simplify this even more by replacing it with the following:

@FindBy(how=How.CSS,using="div[class=’yt-lockup-tile yt-lockup-video’]")
private List<WebElement> videoElements;

Additionally, you can use @FindAll with multiple @FindBy annotations to look for elements that match any of the given locators:

@FindAll({@FindBy(how=How.ID, using=”username”),
	@FindBy(className=”username-field”)})
private WebElement user_name;

Now that we can represent web pages as Java classes and use Page Factory to initialize WebElement variables easily, it is time we see how we can write simple Selenium tests using PO and PF patterns.

Simple Test Automation Project in Java

For our simple project let’s automate developer sign up for Toptal. To do that, we need to automate the following steps:

  • Visit www.toptal.com
  • Click on the “Apply As A Developer” button
  • On Portal Page first check if it’s opened
  • Click on the “Join Toptal” button
  • Fill out the form
  • Submit the form by clicking on “Join Toptal” button

Setting Up a Project

  • Download and install Java JDK
  • Download and install InteliJ Idea
  • Create a new Maven project
  • Link “Project SDK” to your JDK, e.g.: on Windows “C:\Program Files\Java\jdkxxx”
  • Setup groupId and artifactId:
<groupId>SeleniumTEST</groupId>
<artifactId>Test</artifactId>
  • Add dependencies Selenium and JUnit Maven in your project POM file
   <dependencies>
        <!-- JUnit -->         
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

        <!-- Selenium -->

        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-firefox-driver</artifactId>
            <version>${selenium.version}</version>
        </dependency>

        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-support</artifactId>
            <version>${selenium.version}</version>
        </dependency>

        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium.version}</version>
        </dependency>

    </dependencies>

Replace Selenium version and JUnit Version with latest version numbers that can be found by searching for JUnit Maven on Google and on Selenium site.

At this point, if auto build is enabled, dependencies should start downloading automatically. If not, just activate Plugins > install > install:install under the Maven Projects panel on the right side of your IntelliJ Idea IDE.

Once the project has been bootstrapped, we can start creating our test package under “src/test/java”. Name the package “com.toptal”, and create two more packages under it: “com.toptal.webpages” and “com.toptal.tests”.

We will keep our Page Object/Page Factory classes under “com.toptal.webpages” and the test routines under “com.toptal.tests”.

Now, we can start creating our Page Object classes.

HomePage Page Object

The very first one we need to implement is for Toptal’s homepage (www.toptal.com). Create a class under “com.toptal.webpages” and name it “HomePage”.

package com.toptal.webpages;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;
import org.openqa.selenium.support.PageFactory;

public class HomePage {
   private WebDriver driver;

   //Page URL
   private static String PAGE_URL="https://www.toptal.com";

   //Locators

   //Apply as Developer Button
   @FindBy(how = How.LINK_TEXT, using = "APPLY AS A DEVELOPER")
   private WebElement developerApplyButton;

   //Constructor
   public HomePage(WebDriver driver){
       this.driver=driver;
       driver.get(PAGE_URL);
       //Initialise Elements
       PageFactory.initElements(driver, this);
   }

   public void clickOnDeveloperApplyButton(){

       developerApplyButton.click();

   }
}

Determining Element Locators

On Toptal’s homepage we are interested about one element in particular, and that is the “Apply as a Developer” button. We can find this element by matching the text, which is what we are doing above. While modeling web pages as Page Object classes, finding and identifying elements can often become a chore. With Google Chrome or Firefox’s debugging tools, this can be made easier. By right clicking on any element on a page, you can activate the “Inspect Element” option from the context menu to find out detailed information about the element.

One common (and my preferred) way is to find elements using Firefox’s FireBug extension, in combination with Firefox web driver in Selenium. After installing and enabling FireBug extension, you can right click on the page and select “Inspect element with FireBug” to open FireBug. From the HTML tab of FireBug, you can copy the XPath, CSS Path, Tag name or “Id” (if available) of any element on the page.

By copying the XPath of the element in the screenshot above, we can create a WebElement field for it in our Page Object as follows:

@FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1")
WebElement heading;

Or to keep things simple, we can use the tag name “h1” here, as long as it uniquely identifies the element we are interested in:

@FindBy(tagName = "h1")
WebElement heading;

DeveloperPortalPage Page Object

Next, we need a Page Object that represents the developer portal page, one that we can reach by clicking on the “Apply As A Developer” button.

On this page, we have two elements of interest. To determine if the page has loaded, we want to verify the existence of the heading. And we also want a WebElement field for the “Join Toptal” button.

package com.toptal.webpages;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

public class DeveloperPortalPage {
   private WebDriver driver;

   @FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1")
   private WebElement heading;

   @FindBy(linkText = "JOIN TOPTAL")
   private WebElement joinToptalButton;

   //Constructor
   public DeveloperPortalPage (WebDriver driver){
       this.driver=driver;

       //Initialise Elements
       PageFactory.initElements(driver, this);
   }

   //We will use this boolean for assertion. To check if page is opened
   public boolean isPageOpened(){
       return heading.getText().toString().contains("Developer portal");
   }

   public void clikOnJoin(){
       joinToptalButton.click();
   }
}

DeveloperApplyPage Page Object

And finally, for our third and last page object for this project, we define one that represents the page containing developer application form. Since we have to deal with a number of form fields here, we define one WebElement variable for every form field. We find each field by their “id” and we define special setter methods for every field that simulate keystrokes for the corresponding fields.

package com.toptal.webpages;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

public class DeveloperApplyPage {
   private WebDriver driver;

   @FindBy(tagName = "h1")
   WebElement heading;

   @FindBy(id="developer_email")
   WebElement developer_email;

   @FindBy(id = "developer_password")
   WebElement developer_password;

   @FindBy(id = "developer_password_confirmation")
   WebElement developer_password_confirmation;

   @FindBy(id = "developer_full_name")
   WebElement developer_full_name;

   @FindBy(id = "developer_skype")
   WebElement developer_skype;

   @FindBy(id ="save_new_developer")
   WebElement join_toptal_button;


   //Constructor
   public DeveloperApplyPage(WebDriver driver){
       this.driver=driver;

       //Initialise Elements
       PageFactory.initElements(driver, this);
   }

   public void setDeveloper_email(String email){
       developer_email.clear();
       developer_email.sendKeys(email);
   }

   public void setDeveloper_password(String password){
       developer_password.clear();
       developer_password.sendKeys(password);
   }

public void  setDeveloper_password_confirmation(String password_confirmation){
       developer_password_confirmation.clear();
       developer_password_confirmation.sendKeys(password_confirmation);
   }

   public void setDeveloper_full_name (String fullname){
       developer_full_name.clear();
       developer_full_name.sendKeys(fullname);
   }

   public void setDeveloper_skype (String skype){
       developer_skype.clear();
       developer_skype.sendKeys(skype);
   }

   public void clickOnJoin(){
       join_toptal_button.click();
   }
   public boolean isPageOpened(){
       //Assertion
       return heading.getText().toString().contains("Apply to join our network as a developer");
   }
}

Writing a Simple Test

With Page Object classes representing our pages, and user interactions as their methods, we can now write our simple test routine as a series of simple method calls and assertions.

package com.toptal.tests;

import com.toptal.webpages.DeveloperApplyPage;
import com.toptal.webpages.DeveloperPortalPage;
import com.toptal.webpages.HomePage;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

import java.net.URL;
import java.util.concurrent.TimeUnit;

public class ApplyAsDeveloperTest {
   WebDriver driver;

   @Before
   public void setup(){
       //use FF Driver
       driver = new FirefoxDriver();
       driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
   }

   @Test
   public void applyAsDeveloper() {
       //Create object of HomePage Class
       HomePage home = new HomePage(driver);
       home.clickOnDeveloperApplyButton();

       //Create object of DeveloperPortalPage
       DeveloperPortalPage devportal= new DeveloperPortalPage(driver);

       //Check if page is opened
       Assert.assertTrue(devportal.isPageOpened());

       //Click on Join Toptal
       devportal.clikOnJoin();

       //Create object of DeveloperApplyPage
       DeveloperApplyPage applyPage =new DeveloperApplyPage(driver);

       //Check if page is opened
       Assert.assertTrue(applyPage.isPageOpened());

       //Fill up data
       applyPage.setDeveloper_email("dejan@toptal.com");
       applyPage.setDeveloper_full_name("Dejan Zivanovic Automated Test");
       applyPage.setDeveloper_password("password123");
       applyPage.setDeveloper_password_confirmation("password123");
       applyPage.setDeveloper_skype("automated_test_skype");

       //Click on join
       //applyPage.clickOnJoin(); 
   }

    @After
    public void close(){
          driver.close();
       }
   }

Running the Test

At this point, your project structure should look like this:

If you want to run the test, select “ApplyAsDeveloperTest” from the tree, right click on it and then select Run ‘ApplyAsDeveloperTest’.

Once the test has been run, you can see the results in the lower-left corner of your IDE:

Conclusion

Page Object and Page Factory make it easy to model web pages in Selenium and test them automatically and make the life of both developers and QAs much more simpler. When done right, these Page Object classes can be reused across your entire test suite and to give yourself the opportunity to implement automated Selenium tests for your projects early on, without compromising agile development. By abstracting away user interactions in your page object models and keeping your test routines light and simple, you can adapt your test suite to changing requirements with little effort.

I hope I have managed to show you how to write nice and clean test code that is easy to maintain. I will end the article with my favorite QA quote:

Think twice, code once!

This article originally appeared on Toptal.

Categories
Uncategorized

Is Netflix changing how we get eventertaintment

Recently I opened a Netflix account mainly to watch movies. Until now I haven’t seen a Netflix original TV series or a movie. Then today I saw the trailer for “The Siege of Jadotville” which is a Netflix Original.  I was blown away by the quality of the movie and the story-line.

It then occurred to me that Netflix probably might change the way we preseve media such as Movies, TV series with their Netflix originals.

What I see is that Netflix is already challenging film and media production companies and probably in the future Netflix could able to make partnerships with major production companies. Or maybe major film production companies might get bankrupt with their traditional approach.

 

Categories
Uncategorized

Painting of Poolbeg Lighthouse, Dublin

So I have been going to this Lighthouse located in Dublin. There were quite few nice days with magnificent view of the sea and sky in the evening. I thought to do an oil paint from the photographs I took.
I have been waiting to finish this painting, so I guess I finally finished it today. Had to finish the road that goes towards the lighthouse.

Time to call it done and move to another piece… #oilpainting

A photo posted by Sinaru Gunawardena (@sinaru.g) on

Hope it looks good. 🙂

Where is Poolbeg Lighthouse?

Categories
Uncategorized

Begging for happiness

I’m in the process of creating a new painting with begging as the subject.  There is this old woman who suddenly appeared in Nawala Colombo. I happen to see this woman everyday because she’s just spending time near to where I work. When I look at her eyes, it’s like time has stopped with her and she’s waiting for something. What I think is probably she’s waiting for the day when she finally get through her loneliness. It’s because she was not born begging…

The title I have given for the piece is “Begging for happiness”.

Hope it has come good. The following is the art piece.

begging_for_happiness

 

Categories
Uncategorized

A tv series about art: Power of Art

BBC Power of Art, is TV series that talks about the power of art. It contains 8 episodes in which each episodes a life of a great artist and his paintings are discussed.