Protractor: Connect to MySQL

Hello everyone!

I’m back here to give you some extra tips on Software Testing using Protractor, this time how to connect to a MySQL database when testing your application at localhost.

Node.js is one of the coolest Javascript engines which provides tons of libraries to perform the most varied kinds of tasks. And should you need any task and do not have enough time to work around it using the native library, you may use the vast collection of 3rd party modules available at npm (Node Package Manager).

Today, we are going to connect to a MySQL database using the mysql library, developed by a bunch of developers and which has got only 140 206 downloads last month. Quite a lot don’t you think? And it works amazingly!

First, you will need to download the library running the following command from your testing root directory:

npm install mysql --save-dev

Remember, the flag

--save-dev

saves the reference to the module into your package.json file, so next time you install your testing system, you should only run:

npm install

And install all your dependencies at once.

Given the above, lets see how to organize the files to keep a modularised testing code. In my last post Protractor: The Page Object Model, I introduced an architectural model to keep your tests organised, and what we are going to need now is a PageObject.js, a testspec.js and a ConnectDatabase.js object.

In your PageObject.js, you are going to define attributes to store web elements and methods to perform actions on those attributes, for example addUser(callback). Please refer to Protractor: The Page Object Model for details.

Your ConnectDatabase.js will require the Node.js module mysql, define connection attributes to hold values and methods to perform actions.

function ConnectDatabase (){
var mysql = require('../../node_modules/mysql');

this.connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'your_db_password',
database: 'your_db_name'
});
};
module.exports = ConnectDatabase;

Now imagine you wish to test if the creation of a user is really adding a new row into your users table in MySQL. You should better count the rows before adding the user and then after. Logically, the math is after = before + 1, and if it is not true, something is wrong.

After creating your connection object, which happens to be a function, you can use it within your testspec.js

//Require and instantiate the page object
var PageObject = require('../pageobjects/PageObject');
var pageObject = new PageObject();
//Database connection
var ConnectDatabase = require('../ConnectDatabase');
var connectDatabase = new ConnectDatabase();
var sql = 'SELECT COUNT(*) AS result FROM users_table';
connectDatabase.connection.connect();
it('Should add user and verify new entry in mysql', function() {
    //Defines variables to hold row count before and after addUser()
    var before;
    var after;
    connectDatabase.connection.query(sql, function(err, rows) {
        if (!err) {
            before = rows[0].result;
        }
    });
    //Inserts user and verifies row insertion
    pageObject.addUser(function() {
        //Counts rows after import
    connectDatabase.connection.query(sql, function(err, rows) {
            if (!err) {
                after = rows[0].result;
            }
            //Tests rows after against rows before addUser()
        expect(after).toBe(before + 1);
    });
    });
}, 10000);

So that’s it guys, hope you enjoyed. Leave your comments, I’ll be glad to help you out. And remember, TESTING IS QUALITY!

Best,

Raf.

Protractor: The Page Object Model

web element functions

This post aims at explaining a Test Automation design pattern I have com across during my work @ CESAR (Recife) writing software to execute E2E Test Automation. The Page Object Model applied to Protractor.

After conceptualizing different tools available for the job, the team opted for using a framework called Protractor [Github], which is an E2E Test Framework for Angular Apps, according to the project’s page http://www.protractortest.org.

Developed to fit Angular Apps testing, it serves as a wrapper for Selenium Webdriver, providing advantages when compared to Selenium alone using Java. Apart from the element locators specific for Angular Apps, one of the main advantages is automatic waiting: it executes the next step of your tests as soon as the page finishes all pending tasks.

But the main purpose of this post is to provide you guys with a practical idea of designing an easily maintainable modularised auto-test suite based on the Page Object model. The tools used for the design of this suite were basically Node.js and Protractor, which wraps WebdriverJS and uses the Jasmine framework for BDD (Behavior Driven Development).

To design a maintainable, decoupled test automation solution, we should consider the same approach used for Java Page Objects. Each page should be encapsulated into their own object and instantiated at runtime. The directory structure destined to hold the Testware is the following. Notice we have used the same Java class notation, naming objects instantiated at runtime with Upper Case.

|---- protractor
   |---- configurationfile.js
   |---- package.json
   |---- data
      |---- set_one.js
      |---- set_two.js
      |---- dumpfile.sql
   |---- pageobjects
      |---- LoginPage.js
      |---- HomePage.js
   |---- connections
      |---- ConnectDatabase.js
      |---- ConnectAMQP.js
   |---- testconditions
      |---- Environment_one.js
      |---- Environment_two.js
   |---- tests
      |---- sanity.js
      |---- functional.js
      |---- feature_uat.js
   |---- node_modules
      |---- jasmine-reporters
      |---- mysql

We are going to use Node.js features to export Javascript objects as functions to the execution environment. For this reason, the design chosen was to define every object as an anonymous function. See below an example of the LoginPage.js.

var LoginPage = function() {
    
    //Constants - could be taken from a test DB for multilevel access test
    const URL = "http://localhost/login";
    const USERNAME = "roger";
    const PASSWORD = "1234";
    const LOGIN_OK = "Login Successful";

    //Attributes - WebElements of the page
    this.usernameElement = element(by.name("field_name_here"));
    this.passwordElement = element(by.id("field_id_here"));
    this.submitElement = element(by.id("field_id_here"));
    this.messageLabel = element(by.id("label_id_here"));

    //Methods - Actions performed at the page using the WebElements
    this.getLoginPage = function() {
        browser.get(URL);
    };
    this.login = function() {
        this.usernameElement.sendKeys(USERNAME).
        then(this.passwordElement.sendKeys(PASSWORD).
            then(this.submitElement.click()
            )
        );
    };
};

//This will export the module to Node.js runtime environment
module.exports = LoginPage;

At the test suite, you will invoke the Page Objects you will need to run you test, in this example a part of my sanity check, sanity.js.

//A page object required and Instantiated
var LoginPage = require("../pageobjects/LoginPage");
var loginPage = new LoginPage();

//The test suite and specs (it)
describe('Sanity Test Suite', function() {
    describe('Login test', function() {
        it('Should login and verify success', function() {
            loginPage.login().then(function() {
                loginPage.messageLabel.getText().then(function(text) {
                    expect(text).toBe(loginPage.LOGIN_OK);
                })
            });
        }, 10000);
    });
});

Every element is a WebElement object, containing methods, such as click(), for example:

pageInstance.pageElement.click();

If you create a method within the page object to print the object “this” to the terminal, you will be able to see all the elements mapped with their methods, for example:

Page Object:

this.printElements = function() {
    console.log(this);
};

Test file:

pageObject.printElements();

What you see is something like this (where passForm is the name of an attribute mapped to a WebElement):

screenshot

All these functions you may use to perform many actions. They all return promises. For a more detailed look over the methods, please check the API.

For a great kick-off on how to use Protractor, checkout this Tutorial.

Best,

Raf.