QUTest™ (pronounced "cutest") is a unit testing harness (a.k.a. unit testing framework), which is specifically designed for deeply embedded systems, but also supports unit testing of embedded code on host computers ("dual targeting"). QUTest™ is the fundamental tooling for Test-Driven Development (TDD) of QP/C/C++ applications, which is a highly recommended best-practice.
In a nutshell, working with QUTest™ is similar to "debugging with printf" (or sprintf
or similar), where you instrument the code to output information about its execution. You then run the code with a controlled set of inputs, and examine the produced output from the printf
s to determine whether the code under test operates correctly. The main differences from using printf
s are: (1) that the much more efficient QP/Spy output mechanism is used instead and (2) that both generating the inputs and the checking of the test outputs are automated.
The process of testing embedded code with QUTest™ involves the following components:
NOTE: Several examples of test fixtures are explained in the QUTest Tutorial. The details of test fixtures are described in QUTest Fixture Reference.
NOTE: Several examples of test scripts are explained in the QUTest Tutorial. The details of test scripts are described in QUTest Script Reference.
The general QUTest™ structure just described corresponds to running tests on an embedded Target. But QUTest™ can also execute tests on the host computer. In that case (shown in the figure below), the test fixture is a host executable that communicates with the QSPY host application via a TCP/IP socket (QSPY started with the -t
command-line option). In this case all QUTest™ components execute on the host computer.
Unlike other existing unit testing harnesses for embedded systems (e.g., Unity or CppUTest) QUTest™ is not based on xUnit that was originally designed to run tests on host computers. Instead, QUTest™ is geared towards unit testing of deeply embedded systems. Here is a list of QUTest™ unique features, specifically designed for this purpose:
NOTE: QUTest™ supports test scripts written in Python (3.3+).
setup()
and teardown()
functions that other test fixtures offer (and of course QUTest supports as well). Clean reset of the Target avoids erroneous tests that implicitly rely on side effects from previously executed code. This is particularly important for embedded systems and for state machines, so that each test can start from a known reset condition.malloc()/free()
in C or new/delete
in C++). This means that you don't need to commit any of your precious embedded RAM to the heap (you can set the heap size to zero) and you don't need to link the heap management code. Avoiding dynamic memory allocation is one of the best practices of real-time embedded programming, which you don't need to compromise to run QUTest.setjmp()()/longjmp()
in C or throw/catch
in C++), which are needed by other test harnesses to discontinue failing tests. QUTest™ test fixtures do not need to discontinue failing tests, because they don't check "testing assertions", so a test fixture does not "know" if it is failing or passing. Should a test fixture crash on the Target, it simply waits for the target reset commanded by a test script.main()
function in a test fixture and in your final application. This means that you can either grow your test fixture into a final application through TDD, or you can more easily add unit tests to an existing application.The qutest.py script can be used standalone, without any installation in your Python system (see Running QUTest™ below).
Alternatively, you can use *your own Python** installation, into which you can install the latest QUTest™ with pip
from the PyPi index by executing the following command:
pip install qutest
If you are using QUTest™ as a standalone Python script, you invoke it as follows:
python3 <path-to-qutest-script>/qutest.py [-x] [test-scripts] [host_exe] [qspy_host[:udp_port]] [qspy_tcp_port]
Alternatively, if you've installed QView™ with pip
, you invoke it as follows:
qutest [-x] [test-scripts] [host_exe] [qspy_host[:udp_port]] [qspy_tcp_port]
-x
- optional flag that causes qutest to exit on first test failure.test_scripts
- optional specification of the Python test scripts to run. If not specified, qutest will try to run all *.py files in the current directory as test scriptshost_exe | "" | DEBUG
- optional specification of the test-fixture compiled for the host (host executable) for testing on the host computer. The placeholder value ""
(empty string) can be used for running test fixtures on an embedded Target. The special value DEBUG means that qutest will run in the "debug mode", in which it will NOT launch the host executables and it will wait for the Target reset and other responses from the Target. If host_exe is not specified, an embedded target is assumed (which is loaded with the test fixture already).qspy_host[:udp_port]
- optional host-name/IP-address:port for the host running the QSPY host utility. If not specified, the default is 'localhost:7701'.tcp_port
- optional the QSpy TCP port number for connecting host executables. If not specified, the default is 6601
.qutest.py
are positional, meaning that an option must be at the specific position in the command-line to be parsed correctly. For example, if you wish to run QUTest on an embedded target at the specified [qspy_host]
host-name, you still need to provide a placeholder for the host executable [host_exe]
("") option, to get to the right position for the [qspy_host]
option.Windows Hosts
python3 %QTOOLS%\qutest\qutest.py
runs all the test scripts (*.py
) in the current directory.
python3 %QTOOLS%\qutest\qutest.py *.py
runs the test scripts (*.py
) in the current directory.
python3 %QTOOLS%\qutest\qutest.py *.py build\dpp.exe
runs the test scripts (*.py
) in the current directory and uses the host executable: build\dpp.exe
python3 %QTOOLS%\qutest\qutest.py *.py "" 192.168.1.100:7705
runs the test scripts (*.py
) in the current directory, without a host executable: (""
), and connects to QSPY at 192.168.1.100:7705
qutest *.py build\dpp.exe 192.168.1.100:7705
runs "qutest" (installed with pip) to execute the test scripts (*.py
) in the current directory, uses the host executable: build\dpp.exe
, and connects to QSPY at 192.168.1.100:7705
qutest *.py build\dpp.exe localhost:7701 6605
runs "qutest" (installed with pip) to execute the test scripts (*.py
) in the current directory, uses the host executable: build\dpp.exe
, and connects to QSPY at localhost:7701
, using the local UDP port 6605
Linux/MacOS Hosts
python3 $(QTOOLS)/qutest/qutest.py
runs all the test scripts (*.py
) in the current directory.
python3 $(QTOOLS)/qutest/utest.py *.py
runs the test scripts (*.py
) in the current directory.
python3 $(QTOOLS)/qutest/qutest.py *.py build/dpp
runs the test scripts (*.py
) in the current directory and uses the host executable: build/dpp
python3 $(QTOOLS)/qutest/utest.py *.py "" 192.168.1.100:7705
runs the test scripts (*.py
) in the current directory, without a host executable: (""
), and connects to QSPY at 192.168.1.100:7705
qutest *.py build/dpp 192.168.1.100:7705
runs "qutest" (installed with pip) to execute the test scripts (*.py
) in the current directory, uses the host executable: build/dpp
, and connects to QSPY at 192.168.1.100:7705
qutest *.py build/dpp localhost:7701 6605
runs "qutest" (installed with pip) to execute the test scripts (*.py
) in the current directory, uses the host executable: build/dpp
, and connects to QSPY at localhost:7701
, using the local UDP port 6605
Next: QUTest™ Tutorial