Creating Unit Test using Qt Test

Introduction

Hello readers, we discussed some basic introduction and concepts about Qt Test in my previous article. If you are new to the Qt Test, I recommend you to click the below link to go through my previous article before continuing this.

In this article, we are going to write an example application with unit test capabilities by using Qt Test Framework.

Creating a Qt Test Application

Creating an Qt application with testing capability is very simple as discussed in the previous article. Qt creator provides project template for creating the application that contains test classes in-turn test the classes of production code. For better understanding, we can discuss with a simple example below:

Create sample MathCalc class, which is a production code given below:

#ifndef MATHCALC_H
#define MATHCALC_H

class MathCalc
{
public:
    MathCalc(int a = 0, int b = 0) : m_nA(a), m_nB(b) { }


    void setData(int a, int b) {
        m_nA = a;
        m_nB = b;
    }

    int Sum() const { return m_nA + m_nB; }
    int Diff() const { return m_nA - m_nB; }
    int Mult() const { return m_nA * m_nB; }
    int Div() const { return m_nA / m_nB; }

private:
    int m_nA;
    int m_nB;
};

#endif // MATHCALC_H

The above MathCalc is the production code needs to be tested. For testing the MathCalc class, we are going to create the new test class called TestMathCalc class.

#include <QtTest>

#include "mathcalc.h"

class TestMathCalc : public QObject
{
    Q_OBJECT

public:
    TestMathCalc();
    ~TestMathCalc();

private slots:
    void test_sum();
    void test_mult();

private:
    MathCalc m_objMathCalc;
};

TestMathCalc::TestMathCalc()
{

}

TestMathCalc::~TestMathCalc()
{

}

void TestMathCalc::test_sum()
{
    // sum default
    QCOMPARE(m_objMathCalc.Sum(), 0 + 0);

    // sum after setting A and B
    const int A = 10;
    const int B = 20;
    m_objMathCalc.setData(A, B);

    QVERIFY2(m_objMathCalc.getA() == A, "Operand A doesn't match");
    QVERIFY2(m_objMathCalc.getB() == B, "Operand B doesn't match");

    QCOMPARE(m_objMathCalc.Sum(), A + B);
}

void TestMathCalc::test_mult()
{
    const int A = 5;
    const int B = 6;
    m_objMathCalc.setData(A, B);

    QCOMPARE(m_objMathCalc.Sum(), A + B);
}

QTEST_APPLESS_MAIN(TestMathCalc)

#include "tst_testmath.moc"

Run the above code & check the output and it will look like below:


Now Change the line number 41 like below to simulate the failure condition. 

    QVERIFY2(m_objMathCalc.getA() == 0, "Operand A doesn't match");

You will get the output like below:


Conclusion

We experimented with a small example unit test project by using Qt Test Framework. This will give an idea and more confidence to you for exploring more, by using Qt Test. 

You can download the complete source code from the GitHub.

Introduction to Qt Test

What is Unit Testing ?

Unit Testing is a process in which a way to test the small pieces of code that can be logically isolated from the system. Piece of code (known as unit) may be an individual function, method, procedure, module, or object. The purpose of task is to validate each unit of the software code performs as expected. Unit Testing is done during the development phase of an application by the developers.

What is Qt Test ?

Qt Test is a unit testing framework released from the Qt developers to test the applications or libraries based on Qt framework. It provides all the functionality commonly found in unit testing frameworks as well as extensions for testing graphical user interfaces.

Create Qt Test Project

Qt Creator facilitate the wizard to create the project for unit testing. You can use the "Auto Test Project" wizard to create the Qt Test project.

  • Select File > New Project > Other Project > Auto Test Project > Choose to create a project with boilerplate code for a Qt test.

                                     

  • In the Project and Test Information dialog, specify settings for the project and test:
    • In the Test framework field, select Qt Test.
    • For a Qt test, select the GUI Application check box to create a Qt application.
    • In 'Test case name' field, enter the name of the test case.
    • Select the 'Requires QApplication' check box to add the include statement for QApplication to the main.cpp file of the project.
    • Select the 'Generate initialization and cleanup code' checkbox to add functions to your test that are executed by the testing framework to initialize and clean up the test.
    • Select the build system to use for building the project: qmake, CMake, or Qbs.


Qt Creator creates the test project in the specified project directory. Edit the .cpp file to add private slots for each test function in your test.

Creating a Test
Creating a test, subclass QObject and add one or more private slots in your test class. Each private slot is a test function in your test. In addition, you can define the following private slots that are not treated as test functions. When present, they will be executed by the testing framework and can be used to initialize and clean up either the entire test or the current test function.

  • initTestCase() will be called before the first test function is executed.
  • initTestCase_data() will be called to create a global test data table.
  • cleanupTestCase() will be called after the last test function was executed.
  • init() will be called before each test function is executed.
  • cleanup() will be called after every test function.

Every test should leave the system in a usable state, so it can be run repeatedly. Cleanup operations should be handled in cleanupTestCase(), so they get run even if the test fails.

QTest Namespace
Qt Test Framework provides QTest namespace and QAbstractItemModelTester, QSignalSpy classes to facilities various functions and declarations for unit testing. 

  • QTest - All public methods are in the QTest namespace. 
  • QSignalSpy - Enables introspection for Qt's signals and slots.
  • QAbstractItemModelTester - Allows for non-destructive testing of item models.
Finally....
I am so exhausted with lots of theory. So lets break it here and we meet in the next session with some interesting examples for better understanding. Bye...

Validating Inputs using Qt

Introduction:

Validating the inputs is real and challenging task for the software developer. Users is always seem to type in inputs the developer did not expect. It's not really their fault, the developer needs to validate the input or allow the user to give the input that what you expect.

Qt developers have a wide range of input validators to choose from. In this article I'll review the various input validators available, and show the ways to help our users using QCompleter.


Mask Based Validation:

The easiest way to validate input in a QLineEdit is to use a mask. A mask is a special string that tells the line edit what type of characters the user can type next. If a user types a character that is not in range, it is simply ignored by the line edit.

A mask has both normal characters and special character. Each special character represents a character group. For example, the following code creates a QLineEdit that takes a numeric value in the form: +91-99-99-123423 (like mobile numbers in India).

#include <QtGui/QtGui>

int main(argc, **argv)
{
    QApplication app(argc, argv);

    QLineEdit e;
    e.setInputMask("9999-999-9999-999");
    e.show();

    app.exec();
}

Using QValidator:

Qt has an abstract class named QValidator that establishes the interface for validating the data. Two of QValidator's concrete subclasses can be used for numeric range checking: QIntValidator and QDoubleValidator.

QValidator has a pure virtual method called validate() that returns the following enumerated values:

  • Invalid – Not satisfied the required conditions. 
  • Intermediate – Not satisfied the required conditions. However, further inputs may produce an acceptable result. 
  • Acceptable – Satisfies the required conditions.

Regular Expression:

Regular expressions are powerful tools for validating the input. Regular expressions are offered by many languages like C++0x, python, Perl, Java and etc,. Qt provides class called QRegExp, which implements most of the Perl-style extended regular expression.

The QRegExp class provides pattern matching using regular expressions. It fully supports Unicode. QRegExp will expect or built the pattern string to qualify the valid input from expressions, quantifiers, and assertions.

  • Expression: The expression is a simple character. It can also be a set of characters enclosed in square brackets. For Ex: [ABCD] or [A-D].

  • Quantifier: Specify the number of occurrences of an expression that may appear in the matching expression. For Ex: X{1,1} means match one and only one X. X{1,5} means match a sequence of X characters that contains at least one X but no more than five. The below characters are used as a quantifiers. 
    • +        1 or more occurrences.
    • ?         0 or 1 occurrences. 
    • *         0 or more occurrences. 
    • {i,j}    At least i but not more than j occurrences. 

  • Assertions: Specify the boundaries of a matching effort. The below characters are used as an Assertions. 
    • The caret (^), if it is the first character in the regex, indicates that the match starts at the beginning of the string. 
    • The dollar sign ($), when it is the last character in the regex, means that the effort to match must continue to the end of the string. 
    • In addition, there are word boundary (\b) or non-word boundary (\B) assertions that help to focus the attention of the regex. 

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QRegExpValidator>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    int m_BotI, m_TopI;
    double m_BotD, m_TopD;

    QRegExpValidator *m_pValid;

public slots:
   void installMask();
   void showResult();

   void computeResult();

   void installPattern();
   void showRegResult();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    connect(ui->txtMask, SIGNAL(returnPressed()), this, SLOT(installMask()));
    connect(ui->txtInput, SIGNAL(returnPressed()), this, SLOT(showResult()));

    connect(ui->txtMask, SIGNAL(textChanged(QString)), this, SLOT(installMask()));
    connect(ui->txtInput, SIGNAL(textChanged(QString)), this, SLOT(showResult()));

    m_BotI = 5;
    m_TopI = 9;
    m_BotD = 99.9;
    m_TopD = 149.9;

    QIntValidator* iValid(new QIntValidator(m_BotI, m_TopI, this));
    QDoubleValidator* dValid(new QDoubleValidator(m_BotD, m_TopD, 2, this));
    ui->txtWork->setValidator(iValid);
    ui->txtPay->setValidator(dValid);

    connect(ui->txtWork, SIGNAL(returnPressed()), this, SLOT(computeResult()));
    connect(ui->txtPay, SIGNAL(returnPressed()), this, SLOT(computeResult()));


    m_pValid = new QRegExpValidator(this);

    connect(ui->txtPattern, SIGNAL(returnPressed()), this, SLOT(installPattern()));
    connect(ui->txtRegInput, SIGNAL(returnPressed()), this, SLOT(showRegResult()));

    connect(ui->txtPattern, SIGNAL(textChanged(QString)), this, SLOT(installPattern()));
    connect(ui->txtRegInput, SIGNAL(textChanged(QString)), this, SLOT(showRegResult()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::installMask() {
   ui->txtInput->setInputMask(ui->txtMask->text());
}

void MainWindow::showResult() {
    QString str = ui->txtInput->text();
    ui->txtResult->setText(str);
}


void MainWindow::computeResult() {

    double d = ui->txtPay->text().toDouble();
    int i = ui->txtWork->text().toInt();
    double res = d * i;
    ui->txtTotal->setText(QString("%1").arg(res));
}

void MainWindow::installPattern() {
    QRegExp rx(ui->txtPattern->text());
    m_pValid->setRegExp(rx);
    ui->txtRegInput->setValidator(m_pValid);
}

void MainWindow::showRegResult() {

    QString str = ui->txtRegInput->text();
    ui->txtRegResult->setText(str);
}

Note: In Qt 5, the new QRegularExpression class provides a Perl compatible implementation of regular expressions and is recommended in place of QRegExp.

References:

Lambda Expression in C++11

Introduction:

"A lambda expression, sometimes also referred to as a lambda function or (strictly speaking incorrectly, but colloquially) as a lambda, is a simplified notation for defining and using an anonymous function object. Instead of defining a named class with an operator(), later making an object of that class, and finally invoking it, we can use a shorthand."
Bjarne Stroustrup

A lambda function is an inline, anonymous function. The C++ concept of a lambda function originates in the lambda calculus and functional programming. Lambda expressions are typically used to encapsulate algorithms so that they can be passed to another function.



For example, this can be cleaner to read (it keeps everything in one place) and potentially simpler to maintain.

void func(std::vector<T>& v) {

  std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });

}

Lambda functions are just syntactic sugar for anonymous functions.

Syntax:

The syntax for the lambda expression is given below:


You can capture by both reference and value, which you can specify using & and = respectively:

  • [&nCnt] capture by reference
  • [&] captures all variables used in the lambda by reference
  • [=] captures all variables used in the lambda by value
  • [&, nCnt] captures variables like with [&], but nCnt by value
  • [=, &nCnt] captures variables like with [=], but nCnt by reference

The return type can be omitted, If lambda has only one return statement and it has the implicit type of decltype (return_statement). If a lambda is marked mutable (e.g. []() mutable { }) it is allowed to mutate the values that have been captured by value.

In simple cases, the will deduct the return type of the lambda function. However when you start to write more complex lambda you will quickly encounter cases where the return type cannot be deduced by the compiler. To resolve this you are allowed to explicitly specify a return type for a lambda function, using -> T.

Example:

#include <iostream>
#include <vector>
using namespace std;

void func(vector<double> &v, const double& nCnt){

    std::transform(v.begin(), v.end(), v.begin(),

        [nCnt](double d) -> double {

            if (d < nCnt) {
                return 0;
            } else {
                return d;
            }
        });
}

int main(int argc, char *argv[]) {

    std::cout << "Lambda Demo\n";
    double d = 5.0;
    vector<double> v = { 5, -7, 20, -22.4, 3, 45, -80, 55 };

    func(v, d);

    double data;
    foreach (data, v) {
        std::cout << data << " ";
    }
    std::cout <<"\n";
    return 0;
}

Note: Lambda expression has been extended and improved by various proposals in C++14 and C++17. We will discuss these features in another article.

Further References:


1. http://www.stroustrup.com/C++11FAQ.html#lambda
2. http://en.cppreference.com/w/cpp/language/lambda


Type Inference in C++ (auto and decltype)

 auto keyword:

Automatic deduction of data type of an expression in a programming language is called 'Type Inference'. Each data type need to be explicitly declared at compile time to limiting the values of an expression at run time in C++. In C++ 11, Type Inference concept is introduced, and the compiler will deduct the type during the compilation itself on its own. The time for the compilation process increases slightly, but it does not affect run time of program.



Before C++11, the auto keyword was used for storage duration specification. In the new standard its purpose was completely changed. 'auto' keyword is telling the compiler it has to deduce the actual type of a variable that is being declared from its initializer. It can be used when declaring variables in different scopes such as namespaces, blocks or initialization statement of for loops.

auto x = 4;  // x is an int
auto y = 3.37;  // y is an double
auto ptr = &x;  // ptr is pointer to integer
auto f = new foo(); // f is a foo*

Using auto usually means less code (its pritty silly, but it will be sense for some cases). A good use of auto is to avoid long initializations when creating iterators for containers.

std::map<std::string, std::vector<int>> map;
for(auto it = begin(map); it != end(map); ++it) 
{
}

decltype keyword:

The decltype is a keyword introduced in C++11, used to query the type of an expression at compile-type.

It inspects the declared type of an entity or the type of an expression. 'auto' lets you declare a variable with particular type whereas 'decltype' lets you extract the type from the variable, so decltype is sort of an operator that evaluates the type of passed expression.

#include <iostream>
using namespace std;
 
struct A { double x; };
const A* a = new A{0};

int n   = 20;

const int& foo() {
     return n;
}

const int zoo() {
 return n;
}

int main() {

 decltype(n) i;        // type is int
 cout <<  typeid(i).name() << endl;
 
 decltype(foo()) j = foo(); // type is const int&
    cout <<  typeid(j).name() << ":" << j << endl;
 
 decltype(zoo()) x;    // type is int
 cout <<  typeid(x).name() << endl; 

 decltype(a->x) y;        // type of y is double (declared type)
 cout <<  typeid(y).name() << endl;
 y = 55.6;
 
 decltype((a->x)) z = y;  // type of z is const double& (lvalue expression)
    cout <<  typeid(z).name() << ":" << z << endl;
}

Null Pointers

A null value is a special value that means that the pointer is not pointing to anything. A pointer holding a null value is called a null pointer. In C/C++, we can assign a pointer a null value like below:

int *ptr(0);  // ptr is now a null pointer
int *ptr(NULL); // assign address 0 to ptr

In C, defines a special preprocessor macro called NULL and the value is 0. It is not part of C++ technically. So try to avoid using this in C++. Note that the value of 0 isn’t a pointer type and assigning 0 to the pointer is not technically correct, because the compiler can’t tell whether we mean a null pointer or the integer 0.


To resolve these issues, C++11 introduces a new keyword called nullptr. nullptr is both a keyword and an rvalue constant, much like the boolean keywords true and false are.

int *ptr = nullptr;

C++ will implicitly convert nullptr to any pointer type. So in the above example, nullptr is implicitly converted to an integer pointer, and then the value of nullptr (0) assigned to ptr.

For Example:

#include <iostream>
 
void checkPtr(int *ptr) {

    if (ptr)
        std::cout << "You passed in " << *ptr << '\n';
    else
        std::cout << "You passed in a null pointer\n";
}
 
int main()
{
    int *myPtr = nullptr;
 
   checkPtr(myPtr); // the argument is definitely a null pointer (not an integer)
 
    return 0;
}

C++11 also introduces a new type called std::nullptr_t and it is not a value. It can only hold one value called nullptr. It will be useful when you whant to write a function that accepts a nullptr argument.

For Example:

#include <iostream>
#include <cstddef> // for std::nullptr_t
 
void checkPtr(std::nullptr_t ptr)
{
    std::cout << "checkPtr() Called\n";
}
 
int main()
{
    checkPtr(nullptr); // call checkPtr with an argument of type std::nullptr_t
 
    return 0;
}

Note: In C++11, use nullptr to initialize the pointer not the keyword NULL.

QTimer - How to work with timer

Introduction:

A timer is a specialized type of clock for measuring time intervals. In the software development, timer is used to call the particular task which needs to run at a specific interval. Timer will independently count the ticks and if it reaches the specified tick value, then it will trigger the event which will be useful for programmer to detect and call the specific functionally to execute. Qt provides a specialized class called QTimer for achieving this concepts.


QTimer:

The QTimer class provides a high-level programming interface for timers. To use it, create a QTimer, connect its timeout() signal to the appropriate slots, and call start(). From then on, it will emit the timeout() signal at constant intervals.

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
    timer->start(1000);

It's good practice to give a parent to your QTimer to use Qt's memory management system. The above code will call the 'update()' function in every 1000ms.

An alternative to using QTimer is to call QObject::startTimer() for your object and reimplement the QObject::timerEvent() event handler in your class (which must inherit QObject). The disadvantage is that timerEvent() does not support such high-level features.

The accuracy of timers depends on the underlying operating system and hardware. Most platforms support a resolution of 1 millisecond, though the accuracy of the timer will not equal this resolution in many real-world situations.

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H


#include <QMainWindow>
#include <QTimer>
#include <QTime>


namespace Ui {
class MainWindow;
}


class MainWindow : public QMainWindow
{
    Q_OBJECT


public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();


private:
    Ui::MainWindow *ui;
    QTimer *timer;


    QTime tt;


public slots:
    void timerUpdate();
};


#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);


    ui->lblTime->setText(tt.currentTime().toString("hh:mm:ss"));


    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(timerUpdate()));
    timer->start(1000);
}


MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::timerUpdate()
{
    tt.addSecs(1);
    ui->lblTime->setText(tt.currentTime().toString("hh:mm:ss"));
}

Catagory

More »

Catagory