Protractor E2E - How Do You Manage Database?
Asked Answered
P

5

22

I'm currently leaning on the Node + Angular stack and utilising Karma and Protractor for testing.

I'm currently having a hard time figuring out how to handle E2E tests that create and edit data, as well the need to have a expected data loaded.

Searching google turns up lots of various custom methods. I often read 'you should set-up your data' or 'just create a mock' without going into a bit more detail on a common process. Others put in too much overhead in creating a whole new mock module from scratch.

I would just to know how are people currently doing it, and is there a standard for this? Or do people tend to just mock the back-end? Mocking the back-end doesn't seem to be simple either like in Karma since your in the browser scope.

I am as expected using MongoDB, so would be nice to get some direction on what other doing in this scenario. Particularly automating loading of fixtures and database clean-up through Protractor would be good.

Park answered 12/8, 2014 at 23:38 Comment(0)
S
2

There are three ways you can achieve this:

1) The first way is to write easy custom APIs for testing that you can use before your protractor tests starts (protractor config file onPrepare). On the onPrepare you can run a script that creates data in your database that you will later use for testing purposes. That script should contain the logic to push the database entries you require to the database. You can also make this script to run on CI before your protractor tests CI phase starts. This way you will have all the database entries you require loaded onto the database before the tests start. The cons of this is that your tests will take a bit longer to start. Another con would be that if there is setup in between tests, you'd have to run the setup between tests.

2) Second way of doing this would be by using the npm package ng-apimock. I would recommend against using this because mocking is not a good idea for e2e tests. Even so, using ng-apimock will allow you to define API values that you want to mock and push them to a mock server. Then you can use that value later on in your tests using selectScenario. There is a protractor ngapimock plugin that you can use to achieve this. Also a functionality called pass through will allow your API response to not use the mock but use the actual API backend. This tool is used for faster frontend development in case backend is not ready yet. If you are using ngapimock, make sure you maintain your mocks periodically else the tests will fail.

3) Third way of doing this would be by initialising a database that is going to be used only for testing. This means that when you initialise your database, just make sure that only the data you need for testing is present. Run the tests and then destroy the database. This way you will need to maintain your database startup script to add different values into different tables.

Hope this helps.

Santos answered 23/12, 2019 at 9:51 Comment(0)
K
1

Protractor is for e2e testing only. that means, it hast nothing to do with your database. you could use a task runner (like grunt or gulp) to clean-up and fill your database and after that let the task runner start your protractor tests (i never did the last, but i think it is possible). well, i know it's not the answer you were aiming to, but maybe i could point you to the right direction.

Kassa answered 13/8, 2014 at 12:3 Comment(1)
I guess using Grunt could be an option, even though it is external to Protractor. I understand what you mean that it has nothing to do with DB, but since you E2E generally tests the 'whole' stack, that naturally includes the DB, especially when your site is state and data-driven. So we need to ensure 'repeatability' which means I have to consider the database, hence my problem here.Park
S
0

You may manage your database through your REST API (web service).

I want to show an example, how to delete user from database using web service with protractor. In my example, I use Oracle as database.

class OracleDatabaseAccess {
    private readonly BASE_API_URL: string = browser.params.someUrlToYourRest;

    public request<T>(query: string): promise.Promise<T[]> {
        return this.get<T[]>(this.BASE_API_URL, 'sql?query=' + this.fixQuery(query));
    }
    public update<T>(query: string): promise.Promise<T[]> {
        return this.get<T[]>(this.BASE_API_URL, 'update?query=' + this.fixQuery(query));
    }
    public get<T>(url: string, path: string): promise.Promise<T>  {
        const http = new HttpClient(url);
        http.failOnHttpError = true;
        const responsePromise: ResponsePromise = http.get(path);
        return responsePromise.body.then(body => {
            return JSON.parse(body.toString());
        })  as promise.Promise<T>;
    }
    private fixQuery(query: string): string {
        if (query.includes('%')) {
            query = query.split('%').join('%25');
        }
        if (query.includes(';')) {
            query = query.replace(';', '');
        }
        return query;
    }
}

class Queries {
    private oracleDataBaseAccess: OracleDatabaseAccess = new OracleDatabaseAccess();

    deleteUser(userId: string): promise.Promise<{}> {
        return this.oracleDataBaseAccess.update(`delete from users where userId='${userId}'`);
    }
}

Through using request method you can select records from database. Also, using update method you can insert data.

You can use Queries in your describe in beforeAll or afterAll. For example, on beforeAll you create some users, and on afterAll you delete them.

Sass answered 22/9, 2019 at 8:59 Comment(0)
U
0

Mostly you will give to create a separate environment for testing end-to-end tests.

Where after every text execution you need to reset your db with your database reset script

Ultrafilter answered 22/9, 2019 at 9:3 Comment(0)
K
-1

We use the oracledb npm library to perform the data creation and test data setup.

The below code is used to do the same

import * as oracledb from 'oracledb';

connectDb(username: string, password: string, SID: string, setting_name: string, value: string) {
    return browser.driver.sleep(1000).then(() => {
      oracledb.getConnection(
        {
          user: username,
          password: password,
          connectString: SID
        },
        function (err, connection) {
          if (err) {
            console.error(err.message);
            return;
          }
          connection.execute(
            // The statement to execute
            
            //<Place the Query that needs to be executed>,
           UPDATE <table name> SET VALUE=:value WHERE NAME=:setting_name
            {
            //The paramaters to be substituted in the query.
              value: value,
              setting_name: setting_name
            },
            { autoCommit: true },

            // Optional execute options argument, such as the query result format
            // or whether to get extra metadata
            // { outFormat: oracledb.OBJECT, extendedMetaData: true },

            // The callback function handles the SQL execution results
            function (error, result) {
              if (error) {
                console.error(error.message);
                doRelease(connection);
                return;
              }
              doRelease(connection);
            });

        });
    });
    function doRelease(connection) {
      connection.close(
        function (err) {
          if (err) {
            console.error(err.message);
          }
        });
    }
  }
Krishnakrishnah answered 17/12, 2020 at 8:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.