Developing Cross Platform Application using Qt, PyQt and PySide : Test Driven Development and Unit Testing - Part 3 of 5

Discipline, I say! - Mark Pilgrim in Dive Into Python 3

Discipline, I say! Smile - Mark Pilgrim in Dive Into Python 3

Throughout my articles in this site, this is the first article that started without a fancy image : I started it with a quote from Mark Pilgrim's book, Dive Into Python 3, Chapter 9 - Unit Testing. There are two things that drive the decision to include this quote:

  1. Although my bachelor's thesis was about Test Driven Development (TDD) in Extreme Programming using Java, in my professional career as a software developer, it's seldom that I really develop application using TDD in mind. Most of the time,  after the initial requirement gathering, I brutally jump to design the application GUI using current IDE being used. Not entirely a bad practice, as sometimes user really need to quickly presented with the application interface, even though  it's a dummy one. But if things are not that hectic, it's always a good idea to start with the test-first programming/TDD style, as follows:
    • Create unit test of application features about to be implemented,
    • Run it and see it fail (because the feature is not there yet!),
    • Implement the features to make the test pass,
    • Refactor it to have a cleaner code and repeat the process until all features were implemented.
    The beauty of this technique is that, the core of your application features will not get intertwined with the application GUI (imagine having a login that coded directly to the login button. What if we want the login method to be executed from behind a Web API?). A code that follow TDD best practices will be a highly isolated and independent code. Which is truly a great thing, because as the application GUI design undergo changes through out its development, the features were already safe and kept in isolation.
  2. A user in reddit, replied in my post about how to best teach kids in programming with an inspiring comment, "I think we started writing python when they were about 13. And we focused on writing code well - unit test harnesses, data validation, etc from the very beginning". Although I have this vague idea in mind, I never really come close to apply this principles in teaching kids how to code. Exploring unit testing technique in C++/Python will surely be an important skill in my passion of teaching kids how to code.

Before jumping to the topic on how to build the application GUI with Qt using Qt Designer and use the resultant *.ui file into a C++ or Python application, I would like to preface it with an important concept in software development methodology : Test Driven Development and Unit Testing using either C++ or Python. To streamlined the discussion, I have already summarized the principles of TDD in the above passage, and not about to dive any further (except by giving credit to Kent Beck who developed/rediscovered the technique). In the rest of the article we are about to have a practical hands-on in developing unit testing code using either C++ (or to be exact using QtTest) and Python.

Let's explore it!

 

Identifying Application Features

From our previous article, we have already defined an important piece of feature from our overall application features: user authentication. If we are not about to use TDD principles, we will directly laying out user interface elements using a GUI designer. As we will approach the development of this application using TDD, we ought to start its development using test first approach. And as usual, in this series we will approach it using two programming languages : C++ and Python. As much as possible we will use Qt related technology for its unit test development. Luckily, they are all available.

One important note though, in this article we are still talking about unit testing of our application code, and not unit testing our application GUI. Because of this, any C++ and Python unit testing framework should do just fine. But, as Qt already equipped itself with unit testing classes in QtTest, we will use it. And as for the Python part itself, we will use PyUnit, the standard Python unit testing framework, which is a Python version of the infamous JUnit,authored by Kent Beck and Erich Gamma. Throughout this article we will gain insight of how test-first programming was applied to the development of user authentication feature of our application. Later on in this article series, we may talked about unit testing of our application GUI, that already well supported by Qt and then made available in Python by PyQt/PySide.

C++ Unit Testing with QtTest

Red Green Refactor! Image taken from EffectiveSoftwareDesign.com

Red Green Refactor!
Image taken from EffectiveSoftwareDesign.com

As a first clue, you don't have to use QtTest to unit test your C++ Qt application. But, nevertheless in this article we will use QtTest to develop our application user authentication feature by means of first-test programming. In later part we may revised this decision (for the large part, I misses the visual output of Red Green bar when doing TDD in JUnit). And in respond to a fellow marcell from my previous article comments, I will also demonstrate the nature of Qt cross platform development by using Ubuntu 12.0.4 to develop the current unit testing code. This will gives us a better insight on how to manage Qt cross platform application development.

Lets start our test first programming by practice!

Step 1 : Prepare a Unit Testing Project

As demonstrated in our previous article, due to the ultra cross platform nature of Qt, you can prepare a Qt project with or without a specific IDE in any OS (compare this with MS Visual Studio which is functional only on Microsoft Windows). All you need is qmake. Of course an IDE will greatly simplify this process for you, but lets down to the bare bone of preparing a Qt project : using qmake.

A QtTest unit testing project is a Qt console project consisting of at least a *.cpp file that use  QTEST_MAIN macro ,which is a Qt macro that will be expanded into a main() method. Therefor, create a new directory and create this single CPP file:

TestCase01.cpp

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef TEST_CASE_01
#define TEST_CASE_01
#include <QtTest/QtTest>
 
class TestCase01: public QObject
{
    Q_OBJECT
private slots:
    void testAuth();  
};
 
#endif

TestCase01.cpp:

1
2
3
4
5
6
7
8
9
#include "TestCase01.h"
 
void TestCase01::testAuth()
{
    QCOMPARE("expected", "result");
}
 
QTEST_MAIN(TestCase01)
#include "TestCase01.moc"

 

Explanation is commencing ahead, but first, below is a full screenshot of sequence of commands needed to create a working QtTest unit testing project. As usual, to simplify the process, I copy the pre-existing TestCase01.cpp from another directory.

Unit testing with Qt Test

Is your first encounter with C++?  If it is, although I really wanted this blog to served as a great starting point for many technologies, but eventually for an introductory to C++ language, I should delegate that task to another great tutorial sites, such as this site : cplusplus.com. In there, you may directly jumped to the article program structure and then headed to preprocessor directive.

Having jumped from that site and understand how to create a working C++ application, now lets begin with the code explanation. The screenshot above display the result of our failed unit testing code (No problem. It's intentionally left to be failed!). Even tough we only code one test method, testAuth(), QtTest will also run two test method : initTestCase() and  cleanupTestCase(). The first one was called  prior any test methods being called while the last one was called after all test methods was finished executing. We can override these method in case we want to change the behavior of our initialization and cleanup code.

An important thing about any unit testing framework is its assertion method. This is the method that classify whether a certain test method failed or succeed. In our above QtTest unit testing code, we use QCOMPARE() assertion method. This is an assertion method that we call if we want to compare whether an actual value (the first argument) indeed having the same value as an expected value (the second argument). The actual value may came from a variable or a function call return value. This actual value will be our point of development, which we will greatly explore in the subsequent sections. 

Another important thing before we move on to the next section is, what does it means by Q_OBJECT, QTEST_MAIN and also slot from the above code? Those are macros that only relevant to Qt framework. Prior passing it into your C++ compiler, Makefile generated by qmake was already configured to call Qt Meta Object Compiler utility or moc to preprocessed your application sources. If moc found a known Qt macros extension, it will processed it and generate a proper code necessary for successful compilation of your application. For example, the above moc preprocessing will generate a new file TestCase01.moc, which eventually being included into our *.cpp file for a successful compilation.

Q_OBJECT macro will always be needed for any classes in a Qt project. Although it still not a mandatory, but should we want to leverage Qt C++ extension such as signal and slot mechanism (more about that on the subsequent article), meta object system, and parent child memory management, it is obligatory to use Q_OBJECT macro. Any class that use Q_OBJECT macro must also derived itself from QOject to access all of Qt C++ extensions. If you have familiarity of doing any MS Visual C++ MFC development, think of Qt QOject as MFC COject, and you got the idea. Although, one thing to note is, both technology implementation is greatly different: MFC COject didn't require any preprocessing by special utility, where as Qt QOject did.

Step 2 : Test-First Your Authentication Class

Lets concentrate on our yet to develop application feature : user authentication. By thinking in first-test programming, you only think how to call this feature, not yet thinking how to implement it. In other words, lets decide its method signature. And as we use C++, it's a best practice (although not mandatory) for this method to live inside a C++ class. Here is a visual representation of our authentication method living inside a class:

 Bear in mind, I created this visual representation to ease our discussion. In my opinion, it's overkill to create this visualization in a real software development life cycle.

A visual representation of Auth class

Bear in mind that, I created this visual representation to ease our discussion. It's overkill to create this kind of visualization in a real software development life cycle. A well written unit testing code is the one that will effectively replace the above diagram once and for all. If you do need to represent a graphical diagram of your code, just reverse engineered your code using the many UML tool that support reverse engineering code into UML, such as umbrello. Once diagrammed, you can present it to the casual users (one that doesn't move by your code!) or to other fellow programmers to ease them in having a bird eye view of you application architecture, by quickly glimpse at your diagram.

Below is the code for our authentication method. For now, we simply code it to live inside an Auth class. In the future we may (or may not) refactored it to live somewhere else.

Auth.h

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef AUTH_H
#define AUTH_H
 
#include <QObject>
 
class Auth: public QObject
{
        Q_OBJECT
public:
        bool doLogin(QString name, QString pass);
};
#endif

Auth.cpp

1
2
3
4
5
6
#include "Auth.h"
 
bool Auth::doLogin(QString name, QString pass)
{
        return true;
}

The class above was divided into header (*.h) and source file (*.cpp), which is a must in C++ application if you want this class to be used somewhere else. In the above class, our doLogin() method simply return true, to let our unit test passed. Our next task is to implement the real functionality of this method: either by reading user data from a PostgreSQL database or connect to a Web based API (I am thinking of developing an API specs using Django though. But that's another story!).

To use this new class, you have to modify the existing class, TestCase0 as follow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <QtTest/QtTest>
#include "auth.h"
 
class TestCase01: public QObject
{
    Q_OBJECT
private:
    Auth auth;
private slots:
    void testAuth();
};
 
void TestCase01::testAuth()
{
    QCOMPARE(auth.doLogin("eko", "oke"), true);
}
 
QTEST_MAIN(TestCase01)
#include "TestCase01.moc"

We simply include a header to auth.h file and use an Auth object as a private member of our class. And finally, we call auth.doLogin() to test our authentication code.

You will also need to modify the unittesting.pro file to include this new class  by modifying HEADERS and SOURCES configuration as follows:

1
2
HEADERS += Auth.h
SOURCES += TestCase01.cpp Auth.cpp

Finally, as a general rule, if you modify a *.pro file, you will need to re-run qmake to let it generates a new Makefile for you. If failed to do so, you're going to have a confusing time on why your new code seems to be ingored by the build system. And that's not a pleasing experience..

Below is the screenshot of how to add a new class to our existing Qt project:

Adding Auth class to the existing Qt project

Adding Auth class to the existing Qt project

We can observed that although the testAuth() method passed its unit testing execution, but we know for certain that it's just a dummy one. That's the goal of test-first programmin! Now that we know how to make our test method pass (by returning true for existing user with matching password), we can more confident on how to implement this feature. And that's my friends, is the goal to be accomplished in our next mission.

Step 3 : Implement The Core Feature of Authentication Method

Great. We already have a working unit test project and a foundation for our authentication feature. Your next step is to code the core functionality of this feature using your own implementation of choice. I have already made up my mind that our application will be backed up by a local PostgreSQL database. But, as this article already grows length enough (I haven't even talked about unit testing in Python!, I will move a complete explanation of Qt database support to the subsequent article. Please bear with me for a moment... Smile

Anyway, take a clear thought that, even though we haven't got a real authentication feature implemented, we already have it tested first. We have a code that clearly shows how this new feature will be used and highly isolated it into a new class: Auth. This is the essence of what it really means by Test Driven Development. 

Python Unit Testing with PyUnit

Meanwhile in Python...

1
2
3
4
5
6
7
8
import unittest
 
class TestCase01(unittest.TestCase):
    def testAuth(self):
        self.assertEqual("actual", "expected")
 
if __name__ == "__main__":
    unittest.main()

What? It's that simple! Laughing

(You know, I really encourage you to try both C++ and Python unit testing code. Afterward, I am sure you will feel grateful toward Guido van Rossum, our beloved BDFL)

Unit testing code above is using Python built-in unit testing package unittest, that was once known as PyUnit. We construct a new unit test class derived from unittest.TestCase and --as its C++ unit testing class counterpart-- we create a single method named testAuth(). In there, we simply test an assert method assertEqual().

To try the above code you may want to create a new directory, save the above file in a new file named TestFeature01.py and finally run it using the command python TestFeature01, as depicted below:

A very simple and straightforward Python unit testing application

A very simple and straightforward Python unit testing application

Straightforward, isnt' it?

How about adding a new Auth class ? Well, here goes our Auth class saved in auth.py:

1
2
3
class Auth:
    def doLogin(self, username, password):
        return True

Together with the revised TestFeature01.py:

1
2
3
4
5
6
7
8
9
10
import unittest
from auth import Auth
 
class TestCase01(unittest.TestCase):
    def testAuth(self):
        auth = Auth()
        self.assertEqual(auth.doLogin("eko", "oke"), True)
 
if __name__ == "__main__":
    unittest.main()

Below is a screenshot that shows how it is done:

Adding Auth class to the TestFeature01

Adding Auth class to the TestFeature01

I bet you Pythonist love this PyUnit better than QtTest, right? Tongue out

What's Next?

In this article we have went deeper into the world of Qt by understanding how a unit testing application was developed by using our beloved qmake command line utility.You have been introduced to the preliminary intricacy needed in order to understand how a Qt application was developed : the use of meta object compiler utility that will preprocessed any Qt C++ macro extension such as Q_OBJECT and QTEST_MAIN.  We also have had a practical hands-on on how to develop a C++ Qt  application using Test Driven Development/Test First Programming in mind, by means of libtest/QtTest package.

Lastly, I have also introduced you to the way Python approached unit testing by using PyUnit/unittest package.  I am sure it will serve as a great comparative measure of Python easiness toward software developer. Well, I just don't hope that it create a flame war. The right tool for the right job guys! Wink

As always, you can download the current application release in : crossplatformqt-2.zip.

And follow its Github develoment in : pythonthusiast/CrossPlatformQt.

Stay tuned for my next article!





Leave comments

 authimage
  • Hi Andy,

    You are absolutely correct. I will talk about unit testing in Qt GUI (which I think it actually is an integrated testing) somewhere in the near future (maybe in the 5th article). PyQt already have a tight integration with QtTest complete with emulation of mouse movement and click and also keyboard events.

    Those are sufficient for a successful integration testing.

    Thanks for sharing your code!

    • eko
  • Interesting article; I unittest my PyQt code, one thing I wish I had known from the start was how to handle signals and slots appropriately in unittests. I often find myself replacing signals with a MagicMock object from the mock library, and then making assertions about how the emit() method was called:

    e.g (not real code):

    class MyClass(QtCore.QObject):
    someSignal = QtCore.pyqt4signal()

    def foo(self):
    self.someSignal.emit()

    class TestMyClass(unittest2.TestCase):
    def test_signal(self):
    mc = MyClass()
    mc.someSignal = mock.MagicMock()
    mc.foo()
    self.assertEqual(mc.someSignal.emit.call_count, 1)

    • Andy

Copyright(c) 2014 - PythonBlogs.com
By using this website, you signify your acceptance of Terms and Conditions and Privacy Policy
All rights reserved