Showing posts with label qt. Show all posts
Showing posts with label qt. Show all posts

Simple Unit Test With Qt, Continuous integration with Travis-Ci and GitHub

Simple Unit Test With Qt, Continuous integration with Travis-Ci and GitHub

Simple Unit Test With Qt, Continuous integration with Travis-Ci and GitHub

1. Overview

This tutorial is about unit test with Qt (is called QtTest)
We will use QtCreator to write and compile. After that, we will push to GitHub and test the result with Travis-CI.

The entire git project can be downloaded from here

Before we start, I just want to let you know that we gonna create a class hierarchy like this.

2. Create Debug/Release Build

In this is debug/release build, there are main.cpp file and MathOperation class. This is without the unit test.

2.1 Create a Class

We will just create a class called MathOperation and insert a simple function for adding two number.

<detail are omitted for clarity>
int MathOperation::addTwoNumber(int x, int y)
{
    return x + y;
}

2.2 Create a main.cpp

Then we will just write a main.cpp file.

Note: There are two main.cpp files. One for debug/release build and one for test build. Look at the class hierarchy.

        <detail are omitted for clarity>
        MathOperation mathOperation;
        int result = mathOperation.addTwoNumber(3,4);
        qDebug() << "The Result is " << result;

Nothing fancy. We have one class for math operation and add two numbers. Simple.

3. Create Test Build

The reason I want to make different builds is that I want to decouple test header files from normal build so that they won’t disturb each others. In this Test build, there are `UnitTest’ class, ‘TestMathOperation’ class and ‘main.cpp’. UnitTest class will host all the tests (here we only have one test). TestMathOpeation class will be responsible for testing the function from MathOperation. main.cpp will be there to just run all the test.

To demonstrate the QtTest, we will test the function addTwoNumber(int x, int y)

3.1 TestMathOperation.h

We will look at some of the requirements for QtTest. First, test class have to extend from QObject. Then test function has to be slots. Finally, we need <QtTest> header file for testing.

#include <QtTest>
#include "MathOperation.h"
class TestMathOperation: public QObject {
    Q_OBJECT
public:
    TestMathOperation();
private slots:
    void testAddition();
};

3.2 TestMathOperation.cpp

This is the one that actually test the function. We will use macro QCOMPARE to test addition operation. We want to make sure that 2+3 is actually 5. If you put other than 5, the test will fail.

void TestMathOperation::testAddition()
{
    MathOperation mathOperation;
    QCOMPARE(mathOperation.addTwoNumber(2,3),5); //QCOMPARE( actual, expected)
}

4. GitHub and Travis-CI

Here you just need to create a repo, add all the necessary files for the project and add .travis.yml to the repo before you push to github.

# The Trusty beta Build Environment
sudo: required
dist: trusty

install:
  - sudo apt-get install -y qt5-default qttools5-dev-tools #install necessary Qt files

script:
  - qmake "CONFIG+=test" SimpleQtTest.pro #we gonna compile for Unit test first
  - make
  - ./SimpleQtTest #run unit test
  - rm Makefile SimpleQtTest *.o #remove files (which are generated from unit test) for next build
  - qmake "CONFIG+=debug" SimpleQtTest.pro #we gonna compile for actual program
  - make
  - ./SimpleQtTest #run actual program

After you push to your github repo, you will see all the test passing (see the log in Travis-CI) and finally you will see build|passing icon in Travis-CI.

Note: If you want to run on local machine without Travis-CI, you can follow exact command from travis.yml file

4.1 Build and Test on Local Machine

If you want to test locally, we first get necessary dependencies (assume here that you havn’t install QtCreator yet).

$ sudo apt-get install -y qt5-default qttools5-dev-tools

In your project folder, run the following commands to run unit test. All your test should pass.

$ qmake "CONFIG+=test" SimpleQtTest.pro
$ make
$ ./SimpleQtTest

After the last command, you will set the test result like this.

********* Start testing of TestMathOperation *********
Config: Using QtTest library 5.2.1, Qt 5.2.1
PASS   : TestMathOperation::initTestCase()
PASS   : TestMathOperation::testAddition()
PASS   : TestMathOperation::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped
********* Finished testing of TestMathOperation *********

Next, we will just compile our debug build and run.

$ rm Makefile SimpleQtTest *.o 
$ qmake "CONFIG+=debug" SimpleQtTest.pro 
$ make
$ ./SimpleQtTest 

And the final output is,

The Result is  7

5. Reference

  1. Unit testing in Qt using QtTest
  2. Qt Test

undefined reference due to re-factoring or deleting GUI element in QT

“undefined reference” in moc_mainwindow after re-factoring or deleting GUI element



1. remove associate slots from the code
2. remove everything from from shadow build (WARNING: Do NOT remove source code; shadow build directory is different from directory where source code is located)

QT database; create table with helper class






The complete project can be downloaded from here. Instead of writing very messy code in mainwidow.h, the helper class is to minimize coding and to improve clarity. 

Function: 
(1) To open database at start up
(2) To create or delete a table from database



At mainwindow.h
(1) 'database' file is passed to setDataBasePath. This will return bool operator and check whether the database can be opened or not. 'database.db' is created at application folder.

(2) When "Create Table" is clicked, status of 'Creation of Table' changed to 'Table Created'. If table already exists at database, the status changes to 'Failed to create table'.

(3) Similarly, when "Drop Table" is clicked, status of 'Creation of Table' changed to 'Table Dropped'. If table DOES NOT exist at database, the status changes to 'Failed to drop the table'.

(4) Take note that, same table name is created and deleted during this program.


#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    if (dBH.setDataBasePath("database")) {
        ui->label_status->setText("Connected");
    } else {
        ui->label_status->setText("Failed");
    }
}
MainWindow::~MainWindow() {
    delete ui;
}
void MainWindow::on_createTable_clicked() {
    bool isTableCreated = dBH.createTable();
    if (isTableCreated) {
        ui->status_tableCreated->setText("Table Created");
    }
    else {
        ui->status_tableCreated->setText("Failed to create table");
    }
}
void MainWindow::on_dropTable_clicked() {
    bool isTableDropped = dBH.dropTable();
    if (isTableDropped) {
        ui->status_tableCreated->setText("Table Dropped");
    }
    else {
        ui->status_tableCreated->setText("Failed to drop the table");
    }
}













At databasehelper.h, all of the necessary steps and traps are properly commented. Please go through.


#include "databasehelper.h"
#include <QDebug>

DatabaseHelper::DatabaseHelper() //default constructor
{
    //needed it in constructor otherwise, database can't be opened from other function call; werid
    myDataBase = QSqlDatabase::addDatabase("QSQLITE"); //QSLITE must be exact
}

bool DatabaseHelper::setDataBasePath(QString filename) {

//another file *.db will be created if the file is not presented on the system
    QString dbPath = QCoreApplication::applicationDirPath() + "/" + filename
            + ".db";
    myDataBase.setDatabaseName(dbPath);
    if (myDataBase.open()) {

        qDebug() << "databased is opened sucessfully ";
        return true;
    } else {
        qDebug() << "failed to open databased";
        return false;
    }
}

bool DatabaseHelper::createTable() {
    if (myDataBase.isOpen()) {

        QSqlQuery query(myDataBase);
        //you can use "create table if not exists" but it will be always
        //successful to execute this following command

        //Name of table cannot be variable//
        //so have to hard code it//
        //it is just sqlite way//
        bool ret = query.exec("create table studentTable"
                "(id integer primary key, "
                "firstname varchar(20), "
                "lastname varchar(30), "
                "age integer)");

        if (ret) {
            qDebug() << "table created";
            return true;
        } else {
            qDebug() << "faied to crate table";
            return false;
        }
    } else {
        return false;
    }
}

bool DatabaseHelper::dropTable() {

    if (myDataBase.isOpen()) {
        QSqlQuery query(myDataBase);
        bool ret = query.exec("drop table studentTable");

        if (ret) {
            qDebug() << "table dropped";
            return true;
        } else {
            qDebug() << "faied to drop the table";
            return false;
        }
    } else {
        return false; //return false if failed to open database
    }
}

DatabaseHelper::~DatabaseHelper() //default deconstructor
{

}

When you start programming in Qt, if you encounter the following error in ubuntu
cannot find -lGL 
Then install the library by
sudo apt-get install libgl1-mesa-dev

How to install Qt on Ubuntu

Download Qt from here.
Assume it was downloaded to Download folder.
First, go to Download folder.
cd ~/Downloads/
Change the permission of installer.
chmod +x qt-opensource-linux-x64-1.6.0-6-online.run
Then run installer
./qt-opensource-linux-x64-1.6.0-6-online.run

If successfully, it should be like this following image.