Now, I know how the database should be organised. For the implementation, I'll use Test-Driven Development that I described in an earlier blog post for TrafficControl.
The first step is to be able to initiate the database helper at all.
When trying to do that in a local unit test, I wasn't able to initiate the dbHelper class since I couldn't get the context for the app. The reason is probably that I need more resources such as a database that will require an Android environment.
Instead, I was able to use the context in an instrumented unit test. This will be much slower than running locally, since the test needs to be loaded into a real or simulated phone. After some dependency issues for the test APK package that I didn't see for the app, I was finally able to test a simple instrumented test.
In the test, I added a dbHelper that I didn't use. For the local unit test, that step caused a crash.
More information about Android testing can be found here.
The first Test-Driven Development test will be to add columns to the database and check if those columns exist.
Showing posts with label Test. Show all posts
Showing posts with label Test. Show all posts
Saturday, 24 November 2018
Saturday, 16 December 2017
TrafficControl: Testing Asserts
Asserts are used to ensure that events that should never happen will never happen.
I've created a test case, where the program will try to add the same train to a station twice. If that happens, it would be a sign that the program has totally lost control of the network.
When the code tries to add the same train again, the code asserts:
After this dialog, the test sequence stops and the remaining test cases are not run.
I found a workaround in a StackOverflow thread. A message handler will acknowledge the Assert message in the test case and the test suite can continue:
I've created a test case, where the program will try to add the same train to a station twice. If that happens, it would be a sign that the program has totally lost control of the network.
![]() |
| The program adds a train to a station. After that, it tries to add the same train to the station again. The expected result shall be an Assert. |
After this dialog, the test sequence stops and the remaining test cases are not run.
I found a workaround in a StackOverflow thread. A message handler will acknowledge the Assert message in the test case and the test suite can continue:
![]() |
| A message handler is specified before the expected Assert. It acknowledges the assert, without crashing the test. |
Labels:
Assert,
C++,
Google Test,
Programming,
Test,
TrafficControl
Saturday, 9 December 2017
TrafficControl: Opposite Direction TDD Case (2)
After fixing the first issue with trains that can't find adjacent stations, a new issue has been seen.
train::runningState introduces a virtual position, that is
With these changes, the train is able to travel in reversed direction.
A crash was found for train::setTrackPosition(0). When it is updating the position, the method track::getCoordinatesFromPosition asserted if the position was 0, since it was trying to access coordinates at index -1.
The train arrives to Gärsnäs and is supposed to enter the track Sim-GarW at end position (12000). It should move to position 0 and enter Simrishamn. However, it enters the track at start position (Simrishamn) and moves towards Gärsnäs.
The train state model needs to handle the reversed scenario. A bool reversedTraffic with a get/set method is added to the track object.
I made changes in the methods train::readyToRunning, train::runningState and train::runningToOpening.
![]() |
| The train needs to consider its direction (normal or reversed) when entering a track. |
- virtualPosition = position, if train isn't travelling in reversed direction,
- virtualPosition = track length - position, if train is travelling in reversed direction
With these changes, the train is able to travel in reversed direction.
A crash was found for train::setTrackPosition(0). When it is updating the position, the method track::getCoordinatesFromPosition asserted if the position was 0, since it was trying to access coordinates at index -1.
Saturday, 2 December 2017
TrafficControl: Opposite Direction TDD Case (1)
One big stub in trafficControl is that it currently only allows trains to travel on tracks in the intended direction (position 0 up to the end).
To enable trains to move in reversed directions, I will follow the Test-Driven-Development method.
The first iteration is to test a train on a station with one leaving reversed track. The track leaves Simrishamn (start) and arrives to Gärsnäs (end). A train in Gärsnäs that shall go to Simrishamn should find the track and travel in reverse on it to Simrishamn.
In this video, the train was supposed to travel back from Gärsnäs to Simrishamn, but that didn't happen.
The issue is that the method findLeavingTrackIndexToStation only searches for connected tracks where the end station is matching target station. For reversed tracks, the start station will match the target station. No suitable track is found and the function returns UNDEFINED.
To fix the issue, the program will search for a leaving track in the reversed direction in case it doesn't find a track in the ordinary direction.
A corresponding test case has been added:
With this change, the program still fails. After leaving Gärsnäs, the train enters the track near Simrishamn and travels to Gärsnäs. I'll solve that in the next blog post.
To enable trains to move in reversed directions, I will follow the Test-Driven-Development method.
The first iteration is to test a train on a station with one leaving reversed track. The track leaves Simrishamn (start) and arrives to Gärsnäs (end). A train in Gärsnäs that shall go to Simrishamn should find the track and travel in reverse on it to Simrishamn.
In this video, the train was supposed to travel back from Gärsnäs to Simrishamn, but that didn't happen.
The issue is that the method findLeavingTrackIndexToStation only searches for connected tracks where the end station is matching target station. For reversed tracks, the start station will match the target station. No suitable track is found and the function returns UNDEFINED.
To fix the issue, the program will search for a leaving track in the reversed direction in case it doesn't find a track in the ordinary direction.
A corresponding test case has been added:
With this change, the program still fails. After leaving Gärsnäs, the train enters the track near Simrishamn and travels to Gärsnäs. I'll solve that in the next blog post.
Saturday, 14 October 2017
TrafficControl: Using Test Fixtures for Unit Tests
For many unit tests, there are some similarities that can be used to reduce some redundant code.
A test fixture is a class where some pointers to some objects are defined.
In SetUp, the data models are defined, a null pointer is set for the QML part and a networkControl object is created.
The TearDown method will free up the allocated memory and clean up the test environment.
For example, the datamodels for the trafficDataModels (for train, station and track), a pointer to the QML part of the program and the networkControl object.
Google Test offers test fixtures, that makes it possible to setup the same parameters for a set of tests. The test code will be cleaner and each test case will have (more or less) the same setup.
A test fixture is a class where some pointers to some objects are defined.
In SetUp, the data models are defined, a null pointer is set for the QML part and a networkControl object is created.
The TearDown method will free up the allocated memory and clean up the test environment.
The test case is then defined using TEST_F, where the appendix indicates that a test fixture will be used. The test case is networkTest, that was defined above.
In the test case, the networkControl, the needed elements are already defined. The cleanup will be done automatically by TearDown when the test is done.
The concept isn't water-proof, however. The traffic elements (for example trains) has a static int variable to count the total number of trains/stations/tracks. I didn't reset that parameter initially, which caused some test cases to fail.
- Before the first test with the test fixture, that parameter was 0.
- After the first test case, where a train was created, the parameter was set to 1.
- When deleting the train object (called in the networkControl destructor), the static parameter wasn't affected.
- In the second test case, the totalNbrOfTrains was 1, but it should have been 0. This caused the test to fail since the test checked the totalNbrOfTrains parameter.
To fix that issue, the totalNbrOfTrains is set to 0 in the destructor for networkControl.
Monday, 25 September 2017
TrafficControl: Integrating Google Test Into TrafficControl
After some struggle, I was finally successful with integrating Google Test into TrafficControl.
The steps are similar to the QtTest Integration blog post and the previous blog post:
I add two files to the "test" folder:
The mainTest.cpp is identical to the one in the Google Test example. The results can be seen below.
With Google Test, it is easy to sort test cases into different categories. That will be necessary as the number of test cases grow.
In the near future, I'll port the existing QtTest test cases to Google Test and implement the other test cases. I'll keep the existing QtTest files for now, even if I won't use them.
The steps are similar to the QtTest Integration blog post and the previous blog post:
I add two files to the "test" folder:
- mainTest.cpp that is calling the test code and
- tst_tcUnitTests.h, where I have the test code.
The mainTest.cpp is identical to the one in the Google Test example. The results can be seen below.
With Google Test, it is easy to sort test cases into different categories. That will be necessary as the number of test cases grow.
In the near future, I'll port the existing QtTest test cases to Google Test and implement the other test cases. I'll keep the existing QtTest files for now, even if I won't use them.
Wednesday, 20 September 2017
TrafficControl: Analysing a Simple Google Test Project
To understand Google Test, I created a sample test application using Qt 5.9, that is using Microsoft Visual C 2015 and Google Test.
As for other Qt projects, it is a good idea to start looking at the pro file:
![]() |
| += means add module to project when compiling. -= means remove module. |
The qt part is removed for this particular project. I assume that it is since no Qt code is to be tested in this project. It is still possible to compile and test when keeping qt in the project (by commenting out the removal clause on line 7). The same is for the app_bundle tag on line 5.
Google Test is added on the first line, using include statement. Let's have a look at the gtest_dependency.pri file.
The syntax is descibedInitially, there are some checks for whether there is a defined GOOGLETEST_DIR on the computer. If not, the compiler will throw an error message to the build console. If there is such a directory, the test project will be compiled for GTest/GMock.
The test cases are listed in tst_firstgoogletestcase.h as separate functions. The tests are sorted into test cases and test sets, making it possible to have several test sets. This will be useful for me, since I want to sort the test cases in TrafficControl.
The cpp main file initiates the test suite.
In the next blog post, I'll try to import the test cases into the TrafficControl project.
Google Test is added on the first line, using include statement. Let's have a look at the gtest_dependency.pri file.
![]() |
| The syntax is described in the Qt documentation |
The test cases are listed in tst_firstgoogletestcase.h as separate functions. The tests are sorted into test cases and test sets, making it possible to have several test sets. This will be useful for me, since I want to sort the test cases in TrafficControl.
The cpp main file initiates the test suite.
In the next blog post, I'll try to import the test cases into the TrafficControl project.
Tuesday, 5 September 2017
TrafficControl: Testing Google Test
Now when I got QtTest working, I want to investigate how to use Google Test.
Google Test can be used with Qt Creator, and I need to find out how to use that for two reasons:
It's pretty simple to create a stand-alone Google Test application:
In the next blog post, I'll look into the files of the project and try to add test cases, and also to include Google test into TrafficControl.
Google Test can be used with Qt Creator, and I need to find out how to use that for two reasons:
- Google Test might be more powerful with respect to functions,
- Google Test is more well-known than Qt Test and I will probably use it in future projects.
It's pretty simple to create a stand-alone Google Test application:
- First, I cloned cloning the GoogleTest repo using "git clone https://github.com/google/googletest.git",
- After that, I created a Google Test project according to the Qt documentation.
- After compiling, the test is verifying and asserting the value of 0 and 1.
In the next blog post, I'll look into the files of the project and try to add test cases, and also to include Google test into TrafficControl.
Thursday, 31 August 2017
TrafficControl: Test Driven Development
Test Driven Development (TDD) is a process for software development is based on automated tests to develop new functionality.
TDD is based on five steps:
1. Add a test: station_junctionHasNoPassengersAfterAddingTen()
2. Run the test sequence. The target code failed the test as the junction added ten passengers,
3. Fix the target code. I need to both add a method tcStation::isJunction(), and also modify the method tcStation::changeNbrOfPassengers().
4. Re-run the test sequence. Now, the program doesn't change the number of passengers if the station is a junction, as expected.
5. Refactor (Not needed in this particular case)
As with other tools, it is important to be pragmatic and understand when TDD is useful and when it should be avoided. Having a dogmatic view on different tools/methods/frameworks will lead to a lot of time spent on suboptimal solutions.
"You may have the best hammer but not all problems are nails." In some cases "happy testing" / happy coding is necessary. User interface development, for example may be difficult to use with TDD. The TDD process doesn't encourage long-term design considerations, which is crucial for bigger projects.
TDD is based on five steps:
- Define an automated test and add it to the test sequence. Ideally, that test case should make the target code fail.
- Run the test sequence and see that the target code fails on the new test.
- Fix the target code so that the new test case is passing. At this stage, the fix should be "quick and dirty".
- Re-run the test sequence.
- Refactor the code. Here, the fix in step 3 should be refactored.
1. Add a test: station_junctionHasNoPassengersAfterAddingTen()
2. Run the test sequence. The target code failed the test as the junction added ten passengers,
3. Fix the target code. I need to both add a method tcStation::isJunction(), and also modify the method tcStation::changeNbrOfPassengers().
4. Re-run the test sequence. Now, the program doesn't change the number of passengers if the station is a junction, as expected.
5. Refactor (Not needed in this particular case)
As with other tools, it is important to be pragmatic and understand when TDD is useful and when it should be avoided. Having a dogmatic view on different tools/methods/frameworks will lead to a lot of time spent on suboptimal solutions.
"You may have the best hammer but not all problems are nails." In some cases "happy testing" / happy coding is necessary. User interface development, for example may be difficult to use with TDD. The TDD process doesn't encourage long-term design considerations, which is crucial for bigger projects.
Saturday, 26 August 2017
TrafficControl: The First Unit Tests - tcStation
The first unit tests for TrafficControl address one single station object. This will be a brain-storming session, where I add a lot of test cases without implementing them. In those cases, I have commented out the test cases that are yet to be implemented.
![]() |
| The commented test cases are not implemented yet. I also add a short description of the test cases. |
When creating one station object, the default values must be verified:
- Is the station empty initially, both with regard to the number of trains and passengers?
- What happens if invalid coordinates are sent to the constructor of station? If the coordinates are float values, but too big (-100.0, 199)?
- What happens if the station name is already taken?
When creating test cases, I sometimes realise that I need to extend the existing target code to be more testable and observable. That's the very purpose of verification: identifying ways to improve the code.
The test case "station_noCoordinatesAtCreation()" reveals that it useful to add a boolean "hasCoordinates" and a method "tcStation::hasCoordinates();".
The next blog post will introduce Test-Driven Development and show an example of using that methodology.
Monday, 21 August 2017
TrafficControl: Integrating QtTest Into TrafficControl
After some hours looking at official Qt Documentation, internet forum posts and blogs, I found out how to add Qt Tests to the project, and it is quite easy!
A separate folder named test was created for the test code, where I added a C++ file with the name "tst_traffic.cpp".
The project file gets a debug clause (build commands that are only applied for debug builds):
When running QMake the next time for debug builds, the file main.cpp will be removed from sources. Instead the file tst_traffic.cpp will be added to the sources. Also, the testlib is loaded.
The test cases are listed as private slots and implemented as functions. The QVERIFY macro is used for the verdict.
The test report is shown below.
A separate folder named test was created for the test code, where I added a C++ file with the name "tst_traffic.cpp".
The project file gets a debug clause (build commands that are only applied for debug builds):
When running QMake the next time for debug builds, the file main.cpp will be removed from sources. Instead the file tst_traffic.cpp will be added to the sources. Also, the testlib is loaded.
The test cases are listed as private slots and implemented as functions. The QVERIFY macro is used for the verdict.
The test report is shown below.
The next blog posts will cover the new test cases. The target code will be developed in parallel, since I need to change it to be more testable. I'll also use Test Driven Development, which I'll describe in the next blog post.
Wednesday, 16 August 2017
TrafficControl: Some Resources on qTest/Google Test
Qt Applications are normally tested with Google Test plugin or Qt Test. I should learn to master handle both of them.
Qt Test:
There is some official documentation:
http://doc.qt.io/qtcreator/creator-autotest.html
http://doc.qt.io/qt-5/qtest-tutorial.html
http://doc.qt.io/qt-5/qtest-overview.html
Also, there are some blog posts:
http://imaginativethinking.ca/qtest-101-writing-unittests-qt-application/
An old but useful video from the 2010 dev days and also some Qt videos below:
Google Test
I need to learn Google Test and Google Mock Test
The documentation on the Git repo is very useful.
Christian Adam has provided a blog post covering background and a example of setting up a simple test.
Further, a slideshow from a webinar can be found here.
Qt Test:
There is some official documentation:
http://doc.qt.io/qtcreator/creator-autotest.html
http://doc.qt.io/qt-5/qtest-tutorial.html
http://doc.qt.io/qt-5/qtest-overview.html
Also, there are some blog posts:
http://imaginativethinking.ca/qtest-101-writing-unittests-qt-application/
An old but useful video from the 2010 dev days and also some Qt videos below:
Google Test
I need to learn Google Test and Google Mock Test
The documentation on the Git repo is very useful.
Christian Adam has provided a blog post covering background and a example of setting up a simple test.
Further, a slideshow from a webinar can be found here.
Friday, 11 August 2017
TrafficControl: Protecting the Code
After reaching the milestone in the second last blog post, I've done some refactoring of the code to make it cleaner and simpler to maintain:
Initially, I'll learn how to use the test framework in Qt Test. After that, I'll focus on some areas:
- Reviewing parameter names
- Adding Doxygen comments
- Replacing STL vectors with QLists
- Calling the QML JavaScript functions directly
Initially, I'll learn how to use the test framework in Qt Test. After that, I'll focus on some areas:
- How to reset the traffic network between different tests
- Module testing - to verify the modules/functions
- Input files - how to detect problems in the network definition files
- Useability/User Interfaces
- Test-Driven Development - Mainly for different traffic scenarios
- Performance Testing/Optimization
- Memory leak tests
Subscribe to:
Posts (Atom)


























