Qt Creator will not find QML Unit Tests
-
The Problem
I'm trying to develop a testing suite for my project. Right now, I can get pure C++ tests to compile and run within Creator's automated testing tools. Now I'm trying to figure out how to test the QML side of the project. I've gotten to where I can compile a pure QML test, navigate to it in the build directory, and run it using the command line. However, Creator will not run it using the automated tool because it cannot detect the test. How can I get Creator to find and run the QML/Quick unit tests?Project Details
CMake V3.25.1
Qt V6.2.4
Qt Creator V11.0.0 (didn't work on V10 either)The test I'm trying to develop is the Position unit test.
MyProject // Design Studio / Creator project |->cmakeTools // CMake helper functions |->content/.../Lines // UI files | |-> Position.qml // File I'm trying to test |->inc // Global Headers |->lib // Main libraries | |->API // An example library | |->CMakeLists.txt | |->api.cpp | |->api.h |->src // Global sources |->sim // Simulation files |->test // Unit Tests |->API // An example C++ Unit Test | |->CMakeLists.txt | |->ut_api.cpp | |->ut_api.h |->content/.../Lines // UI Unit Tests |->Position // The test that won't work |->CMakeLists.txt |->ut_mycontrol.cpp |->tst_mycontrol.qml // * Every folder shown contains a CMakeLists.txt file, but I haven't shown it for simplicity.
I can configure and build the project as it currently stands, including the Position unit test. However, unlike the API and other C++ unit tests, I cannot run it form Qt Creator using the automated testing tools. Here is a screenshot of the Creator test list.
Position Test Output (Command Line)
./Position_Test Warning: Ignoring WAYLAND_DISPLAY on Gnome. Use QT_QPA_PLATFORM=wayland to run on Wayland anyway. ********* Start testing of position ********* Config: Using QtTest library 6.2.4, Qt 6.2.4 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 10.3.1 20210422 (Red Hat 10.3.1-1)), ubuntu 22.04 PASS : position::Position::initTestCase() FAIL! : position::Position::test_fail() 2 + 2 = 5 Actual (): 4 Expected (): 5 Loc: [/home/jowens/Projects/build-MyProject-amd64-Debug/test/content/Program/Lines/Position/Position_Test_qml/tst_position.qml(15)] PASS : position::Position::test_pass() PASS : position::Position::cleanupTestCase() PASS : position::Position::initTestCase() FAIL! : position::Position::test_fail() 2 + 2 = 5 Actual (): 4 Expected (): 5 Loc: [/home/jowens/Projects/build-MyProject-amd64-Debug/test/content/Program/Lines/Position/tst_Position.qml(15)] PASS : position::Position::test_pass() PASS : position::Position::cleanupTestCase() PASS : position::Position::initTestCase() FAIL! : position::Position::test_fail() 2 + 2 = 5 Actual (): 4 Expected (): 5 Loc: [/home/jowens/Projects/build-MyProject-amd64-Debug/test/content/Program/Lines/Position/tst_position.qml(15)] PASS : position::Position::test_pass() PASS : position::Position::cleanupTestCase() Totals: 9 passed, 3 failed, 0 skipped, 0 blacklisted, 15ms ********* Finished testing of position *********
If what I've found out so far is correct, Qt Tests are detected by finding the
QTest::qExec()
command in the source file. Quick Tests do not seem to have this statement and so are not detected under that heading, but I haven't found any information on how they are supposed to be detected.File Contents
- ut_position.cpp#include <QtQuickTest> #include <QtTest/qtest.h> QUICK_TEST_MAIN(position) #include "ut_position.moc"
- tst_position.qml
import QtQuick 6.2 import QtTest 1.0 import lib.Program import lib.Pose TestCase { name: "Position" function test_pass() { compare(2 + 2, 4, "2 + 2 = 4") } function test_fail() { compare(2 + 2, 5, "2 + 2 = 5") } }
- Position/CMakeLists.txt
cmake_minimum_required(VERSION 3.21.1) project(Position_Test) message(STATUS "Quick Test Root Directory: ${QUICK_TEST_SOURCE_DIR}") set(MSG_HEADER "Processing ${CMAKE_CURRENT_SOURCE_DIR}") message(STATUS "${MSG_HEADER}: --- START ---") message(STATUS "${MSG_HEADER}: Include List [${INCLUDE_DIRECTORIES}]") # Get tools include(${CMAKE_SOURCE_DIR}/cmakeTools) # Set sources, headers, and dependencies set(sources ut_position.cpp ) # set(headers # ut_Position.h # ) set(depend Program Pose Stringable GCodeElement content Poseplugin Programplugin contentplugin MyProjectplugin Qt${QT_VERSION_MAJOR}::QuickTest ) # Create test (located in cmakeTools) generate_test(${PROJECT_NAME} SOURCE ${sources} DEPEND ${depend} ) qt_add_qml_module(${PROJECT_NAME} URI ${PROJECT_NAME}_qml VERSION 1.0 QML_FILES tst_position.qml ) # HEADER ${headers} # Copy qml files to build directory configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tst_position.qml ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) # These are located in cmakeTools (details not provided) include_directory() ListDependencies() message(STATUS "${MSG_HEADER}: ---- END ----")
- MyProject/cmakeTools (used in Position/CMakeLists.txt)
# generate_test # arg1: test name # arg2: sources # arg3: headers # arg4: dependencies function(generate_test) # Parse arguements set(nameProvided FALSE) set(sourcesProvided FALSE) set(headersProvided FALSE) set(dependenciesProvided FALSE) set(parseMode "NAME") set(testName) set(sources) set(headers) set(dependencies) foreach(arg IN LISTS ARGV) # Determine mode if applicable if(${arg} STREQUAL "SOURCE") set(parseMode "SOURCE") elseif(${arg} STREQUAL "HEADER") set(parseMode "HEADER") elseif(${arg} STREQUAL "DEPEND") set(parseMode "DEPEND") else () # No mode change, parse arguement if(${parseMode} STREQUAL "NAME") set(nameProvided TRUE) set(testName ${arg}) elseif(${parseMode} STREQUAL "SOURCE") set(sourcesProvided TRUE) list(APPEND sources ${arg}) elseif(${parseMode} STREQUAL "HEADER") set(headersProvided TRUE) list(APPEND headers ${arg}) elseif(${parseMode} STREQUAL "DEPEND") set(dependenciesProvided FALSE) list(APPEND dependencies ${arg}) endif() endif() endforeach() # Make sure minimum required arguments have been provided if(NOT ${nameProvided}) message(FATAL_ERROR " ${MSG_HEADER}: No test name provided! Please provide a test name as the first argument.") endif() if(NOT ${sourcesProvided}) message(WARNING " ${MSG_HEADER}: No sources provided.") else() message(STATUS "${MSG_HEADER}: Sources: ${sources}") endif() if(NOT ${headersProvided}) message(WARNING " ${MSG_HEADER}: No headers provided.") else() message(STATUS "${MSG_HEADER}: Headers: ${headers}") endif() if(NOT ${dependenciesProvided}) message(STATUS "${MSG_HEADER}: No dependencies provided.") else() message(STATUS "${MSG_HEADER}: Dependencies: ${dependencies}") endif() # Create Test message(STATUS "${MSG_HEADER}: Creating unit test project ${testName}...") project(pro_${testName} LANGUAGES CXX) find_package(Qt6 REQUIRED COMPONENTS Core Gui Test Widgets Quick QuickTest) message(STATUS "${MSG_HEADER}: Creating executable...") qt_add_executable(${testName} ${headers} ${sources}) message(STATUS "${MSG_HEADER}: done.") message(STATUS "${MSG_HEADER}: Adding dependencies...") target_link_libraries(${testName} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Test Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Widgets ${dependencies} ) message(STATUS "${MSG_HEADER}: done.") message(STATUS "${MSG_HEADER}: Creating install path...") install(TARGETS ${testName} RUNTIME DESTINATION "CRWeld_UI/test/${testName}" BUNDLE DESTINATION "CRWeld_UI/test/${testName}" LIBRARY DESTINATION "CRWeld_UI/test/${testName}" ) message(STATUS "${MSG_HEADER}: done.") message(STATUS "${MSG_HEADER}: ${testName} created.") endfunction()
-
The Problem
I'm trying to develop a testing suite for my project. Right now, I can get pure C++ tests to compile and run within Creator's automated testing tools. Now I'm trying to figure out how to test the QML side of the project. I've gotten to where I can compile a pure QML test, navigate to it in the build directory, and run it using the command line. However, Creator will not run it using the automated tool because it cannot detect the test. How can I get Creator to find and run the QML/Quick unit tests?Project Details
CMake V3.25.1
Qt V6.2.4
Qt Creator V11.0.0 (didn't work on V10 either)The test I'm trying to develop is the Position unit test.
MyProject // Design Studio / Creator project |->cmakeTools // CMake helper functions |->content/.../Lines // UI files | |-> Position.qml // File I'm trying to test |->inc // Global Headers |->lib // Main libraries | |->API // An example library | |->CMakeLists.txt | |->api.cpp | |->api.h |->src // Global sources |->sim // Simulation files |->test // Unit Tests |->API // An example C++ Unit Test | |->CMakeLists.txt | |->ut_api.cpp | |->ut_api.h |->content/.../Lines // UI Unit Tests |->Position // The test that won't work |->CMakeLists.txt |->ut_mycontrol.cpp |->tst_mycontrol.qml // * Every folder shown contains a CMakeLists.txt file, but I haven't shown it for simplicity.
I can configure and build the project as it currently stands, including the Position unit test. However, unlike the API and other C++ unit tests, I cannot run it form Qt Creator using the automated testing tools. Here is a screenshot of the Creator test list.
Position Test Output (Command Line)
./Position_Test Warning: Ignoring WAYLAND_DISPLAY on Gnome. Use QT_QPA_PLATFORM=wayland to run on Wayland anyway. ********* Start testing of position ********* Config: Using QtTest library 6.2.4, Qt 6.2.4 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 10.3.1 20210422 (Red Hat 10.3.1-1)), ubuntu 22.04 PASS : position::Position::initTestCase() FAIL! : position::Position::test_fail() 2 + 2 = 5 Actual (): 4 Expected (): 5 Loc: [/home/jowens/Projects/build-MyProject-amd64-Debug/test/content/Program/Lines/Position/Position_Test_qml/tst_position.qml(15)] PASS : position::Position::test_pass() PASS : position::Position::cleanupTestCase() PASS : position::Position::initTestCase() FAIL! : position::Position::test_fail() 2 + 2 = 5 Actual (): 4 Expected (): 5 Loc: [/home/jowens/Projects/build-MyProject-amd64-Debug/test/content/Program/Lines/Position/tst_Position.qml(15)] PASS : position::Position::test_pass() PASS : position::Position::cleanupTestCase() PASS : position::Position::initTestCase() FAIL! : position::Position::test_fail() 2 + 2 = 5 Actual (): 4 Expected (): 5 Loc: [/home/jowens/Projects/build-MyProject-amd64-Debug/test/content/Program/Lines/Position/tst_position.qml(15)] PASS : position::Position::test_pass() PASS : position::Position::cleanupTestCase() Totals: 9 passed, 3 failed, 0 skipped, 0 blacklisted, 15ms ********* Finished testing of position *********
If what I've found out so far is correct, Qt Tests are detected by finding the
QTest::qExec()
command in the source file. Quick Tests do not seem to have this statement and so are not detected under that heading, but I haven't found any information on how they are supposed to be detected.File Contents
- ut_position.cpp#include <QtQuickTest> #include <QtTest/qtest.h> QUICK_TEST_MAIN(position) #include "ut_position.moc"
- tst_position.qml
import QtQuick 6.2 import QtTest 1.0 import lib.Program import lib.Pose TestCase { name: "Position" function test_pass() { compare(2 + 2, 4, "2 + 2 = 4") } function test_fail() { compare(2 + 2, 5, "2 + 2 = 5") } }
- Position/CMakeLists.txt
cmake_minimum_required(VERSION 3.21.1) project(Position_Test) message(STATUS "Quick Test Root Directory: ${QUICK_TEST_SOURCE_DIR}") set(MSG_HEADER "Processing ${CMAKE_CURRENT_SOURCE_DIR}") message(STATUS "${MSG_HEADER}: --- START ---") message(STATUS "${MSG_HEADER}: Include List [${INCLUDE_DIRECTORIES}]") # Get tools include(${CMAKE_SOURCE_DIR}/cmakeTools) # Set sources, headers, and dependencies set(sources ut_position.cpp ) # set(headers # ut_Position.h # ) set(depend Program Pose Stringable GCodeElement content Poseplugin Programplugin contentplugin MyProjectplugin Qt${QT_VERSION_MAJOR}::QuickTest ) # Create test (located in cmakeTools) generate_test(${PROJECT_NAME} SOURCE ${sources} DEPEND ${depend} ) qt_add_qml_module(${PROJECT_NAME} URI ${PROJECT_NAME}_qml VERSION 1.0 QML_FILES tst_position.qml ) # HEADER ${headers} # Copy qml files to build directory configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tst_position.qml ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) # These are located in cmakeTools (details not provided) include_directory() ListDependencies() message(STATUS "${MSG_HEADER}: ---- END ----")
- MyProject/cmakeTools (used in Position/CMakeLists.txt)
# generate_test # arg1: test name # arg2: sources # arg3: headers # arg4: dependencies function(generate_test) # Parse arguements set(nameProvided FALSE) set(sourcesProvided FALSE) set(headersProvided FALSE) set(dependenciesProvided FALSE) set(parseMode "NAME") set(testName) set(sources) set(headers) set(dependencies) foreach(arg IN LISTS ARGV) # Determine mode if applicable if(${arg} STREQUAL "SOURCE") set(parseMode "SOURCE") elseif(${arg} STREQUAL "HEADER") set(parseMode "HEADER") elseif(${arg} STREQUAL "DEPEND") set(parseMode "DEPEND") else () # No mode change, parse arguement if(${parseMode} STREQUAL "NAME") set(nameProvided TRUE) set(testName ${arg}) elseif(${parseMode} STREQUAL "SOURCE") set(sourcesProvided TRUE) list(APPEND sources ${arg}) elseif(${parseMode} STREQUAL "HEADER") set(headersProvided TRUE) list(APPEND headers ${arg}) elseif(${parseMode} STREQUAL "DEPEND") set(dependenciesProvided FALSE) list(APPEND dependencies ${arg}) endif() endif() endforeach() # Make sure minimum required arguments have been provided if(NOT ${nameProvided}) message(FATAL_ERROR " ${MSG_HEADER}: No test name provided! Please provide a test name as the first argument.") endif() if(NOT ${sourcesProvided}) message(WARNING " ${MSG_HEADER}: No sources provided.") else() message(STATUS "${MSG_HEADER}: Sources: ${sources}") endif() if(NOT ${headersProvided}) message(WARNING " ${MSG_HEADER}: No headers provided.") else() message(STATUS "${MSG_HEADER}: Headers: ${headers}") endif() if(NOT ${dependenciesProvided}) message(STATUS "${MSG_HEADER}: No dependencies provided.") else() message(STATUS "${MSG_HEADER}: Dependencies: ${dependencies}") endif() # Create Test message(STATUS "${MSG_HEADER}: Creating unit test project ${testName}...") project(pro_${testName} LANGUAGES CXX) find_package(Qt6 REQUIRED COMPONENTS Core Gui Test Widgets Quick QuickTest) message(STATUS "${MSG_HEADER}: Creating executable...") qt_add_executable(${testName} ${headers} ${sources}) message(STATUS "${MSG_HEADER}: done.") message(STATUS "${MSG_HEADER}: Adding dependencies...") target_link_libraries(${testName} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Test Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Widgets ${dependencies} ) message(STATUS "${MSG_HEADER}: done.") message(STATUS "${MSG_HEADER}: Creating install path...") install(TARGETS ${testName} RUNTIME DESTINATION "CRWeld_UI/test/${testName}" BUNDLE DESTINATION "CRWeld_UI/test/${testName}" LIBRARY DESTINATION "CRWeld_UI/test/${testName}" ) message(STATUS "${MSG_HEADER}: done.") message(STATUS "${MSG_HEADER}: ${testName} created.") endfunction()
@JOwens_RTT Did you find a solution?
-
I could fix it. The solution is pretty simple. Make sure to have in your CMakeLists.txt in root dir
enable_testing()
.// CMakeLists.txt in / cmake_minimum_required(VERSION 3.20) project(QuickTestLab VERSION 0.1 LANGUAGES CXX) enable_testing() set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTORCC ON) find_package(Qt6 6.5 REQUIRED COMPONENTS Quick QuickTest) qt_standard_project_setup(REQUIRES 6.5) qt_add_executable(appQuickTestLab main.cpp ) qt_add_qml_module(appQuickTestLab URI QuickTestLab VERSION 1.0 QML_FILES Main.qml ) ...
Then in
tests/CMakeLists.txt
make sure you've setQUICK_TEST_SOURCE_DIR
.// tests/CMakeLists.txt project(Tests LANGUAGES CXX) enable_testing() add_executable(labelmessage tst_labelmessage.cpp tst_labelmessage.qml ) add_test(NAME labelmessage COMMAND labelmessage) target_link_libraries( labelmessage PRIVATE Qt6::QuickTest PRIVATE Qt6::Qml # PRIVATE appQuickTestLab # PRIVATE appQuickTestLabplugin ) // no need to copy around qml test files for shadow builds - just set the respective define add_definitions(-DQUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") ...
And that's it!
-
@JOwens_RTT Did you find a solution?