diff options
author | Eike Ziller <eike.ziller@qt.io> | 2023-01-25 16:01:27 +0100 |
---|---|---|
committer | Eike Ziller <eike.ziller@qt.io> | 2023-02-28 15:42:48 +0000 |
commit | cace5a57f2a372e5879e12ff0b5d5781f513969b (patch) | |
tree | cb25407542cef685ee7216cc456f1494aa8dd55d | |
parent | 5e866bbf2ff18b006bca05dbc165e2e418cc0ab9 (diff) | |
download | qt-creator-cace5a57f2a372e5879e12ff0b5d5781f513969b.tar.gz |
Plugins: Add documentation about testing
Add information about how to write plugin tests and how to integrate
unit tests.
Change-Id: I13721f03c4c55a265a93f71a7c4d892f3e53a6bb
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
8 files changed, 213 insertions, 2 deletions
diff --git a/doc/qtcreatordev/examples/exampleplugin/CMakeLists.txt b/doc/qtcreatordev/examples/exampleplugin/CMakeLists.txt index 69df3ec4da..65fba00089 100644 --- a/doc/qtcreatordev/examples/exampleplugin/CMakeLists.txt +++ b/doc/qtcreatordev/examples/exampleplugin/CMakeLists.txt @@ -20,6 +20,24 @@ find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED) set(QtX Qt${QT_VERSION_MAJOR}) #! [3] +#! [5] +# Add a CMake option that enables building your plugin with tests. +# You don't want your released plugin binaries to contain tests, +# so make that default to 'NO'. +# Enable tests by passing -DWITH_TESTS=ON to CMake. +option(WITH_TESTS "Builds with tests" NO) + +if(WITH_TESTS) + # Look for QtTest + find_package(${QtX} REQUIRED COMPONENTS Test) + # Tell CMake functions like add_qtc_plugin about the QtTest component. + set(IMPLICIT_DEPENDS Qt::Test) + + # Enable ctest for auto tests. + enable_testing() +endif() +#! [5] + #! [4] add_qtc_plugin(Example PLUGIN_DEPENDS @@ -36,5 +54,16 @@ add_qtc_plugin(Example example.h example_global.h exampleconstants.h + examplefunctions.h ) #! [4] + +#! [6] +# conditionally add auto tests +if(WITH_TESTS) + add_qtc_test(tst_mytest + SOURCES tst_mytest.cpp + DEPENDS Example + ) +endif() +#! [6] diff --git a/doc/qtcreatordev/examples/exampleplugin/example.cpp b/doc/qtcreatordev/examples/exampleplugin/example.cpp index f001fad511..3dd54659f0 100644 --- a/doc/qtcreatordev/examples/exampleplugin/example.cpp +++ b/doc/qtcreatordev/examples/exampleplugin/example.cpp @@ -1,5 +1,7 @@ #include "example.h" + #include "exampleconstants.h" +#include "examplefunctions.h" #include <coreplugin/icore.h> #include <coreplugin/icontext.h> @@ -13,9 +15,31 @@ #include <QMainWindow> #include <QMenu> +//! [test include] +#ifdef WITH_TESTS +#include <QtTest> +#endif +//! [test include] + namespace Example { namespace Internal { +//! [plugin tests] +#ifdef WITH_TESTS +class MyPluginTests : public QObject +{ + Q_OBJECT + +private slots: + void testMyTest() + { + // a failing test + QVERIFY(false); + } +}; +#endif +//! [plugin tests] + ExamplePlugin::ExamplePlugin() { // Create your members @@ -53,6 +77,11 @@ bool ExamplePlugin::initialize(const QStringList &arguments, QString *errorStrin Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu); //! [add menu] +//! [register tests] +#ifdef WITH_TESTS + addTest<MyPluginTests>(); +#endif + //! [register tests] return true; } @@ -81,4 +110,18 @@ void ExamplePlugin::triggerAction() //! [slot implementation] } // namespace Internal + +//! [exported function] +int addOne(int i) +{ + return i; // that is wrong! +} +//! [exported function] + } // namespace Example + +//! [include moc] +#ifdef WITH_TESTS +#include "example.moc" +#endif +//! [include moc] diff --git a/doc/qtcreatordev/examples/exampleplugin/example.h b/doc/qtcreatordev/examples/exampleplugin/example.h index e858e92180..dfde7849a1 100644 --- a/doc/qtcreatordev/examples/exampleplugin/example.h +++ b/doc/qtcreatordev/examples/exampleplugin/example.h @@ -1,7 +1,5 @@ #pragma once -#include "example_global.h" - #include <extensionsystem/iplugin.h> //! [namespaces] diff --git a/doc/qtcreatordev/examples/exampleplugin/example_global.h b/doc/qtcreatordev/examples/exampleplugin/example_global.h index 0ea029c5c7..f051e16dd9 100644 --- a/doc/qtcreatordev/examples/exampleplugin/example_global.h +++ b/doc/qtcreatordev/examples/exampleplugin/example_global.h @@ -3,6 +3,8 @@ #pragma once +#include <qglobal.h> + #if defined(EXAMPLE_LIBRARY) # define EXAMPLE_EXPORT Q_DECL_EXPORT #else diff --git a/doc/qtcreatordev/examples/exampleplugin/examplefunctions.h b/doc/qtcreatordev/examples/exampleplugin/examplefunctions.h new file mode 100644 index 0000000000..7a618cbe57 --- /dev/null +++ b/doc/qtcreatordev/examples/exampleplugin/examplefunctions.h @@ -0,0 +1,9 @@ +#pragma once + +#include "example_global.h" + +namespace Example { + +int EXAMPLE_EXPORT addOne(int i); + +} // namespace Example diff --git a/doc/qtcreatordev/examples/exampleplugin/tst_mytest.cpp b/doc/qtcreatordev/examples/exampleplugin/tst_mytest.cpp new file mode 100644 index 0000000000..12cf7b1e31 --- /dev/null +++ b/doc/qtcreatordev/examples/exampleplugin/tst_mytest.cpp @@ -0,0 +1,21 @@ +#include "examplefunctions.h" + +#include <QtTest> + +class tst_MyTest : public QObject +{ + Q_OBJECT + +private slots: + void mytest(); +}; + +void tst_MyTest::mytest() +{ + // a failing test + QCOMPARE(Example::addOne(1), 2); +} + +QTEST_GUILESS_MAIN(tst_MyTest) + +#include "tst_mytest.moc" diff --git a/doc/qtcreatordev/src/creating-plugins.qdoc b/doc/qtcreatordev/src/creating-plugins.qdoc index ef9ce82959..6a83c6d9bf 100644 --- a/doc/qtcreatordev/src/creating-plugins.qdoc +++ b/doc/qtcreatordev/src/creating-plugins.qdoc @@ -38,6 +38,12 @@ \li \l{Distributing Plugins} \endlist + \section1 Topics + + \list + \li \l{Adding Tests} + \endlist + \section1 Design Principles \list diff --git a/doc/qtcreatordev/src/plugin-tests.qdoc b/doc/qtcreatordev/src/plugin-tests.qdoc new file mode 100644 index 0000000000..78d9c6c4b6 --- /dev/null +++ b/doc/qtcreatordev/src/plugin-tests.qdoc @@ -0,0 +1,103 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page plugin-tests.html + \title Adding Tests + + There are two main ways of testing your plugin code: + + \list + \li \l{Plugin Tests} + \li \l{Auto Tests} + \endlist + + Both have their specific use cases and setup, which is described in the + following sections. + + \section1 Setting up CMake + + Before adding tests, prepare your build files. They need to look for the + QtTest dependency and have a CMake option for building your plugin with + tests: + + \snippet exampleplugin/CMakeLists.txt 5 + + \section1 Plugin Tests + + Plugin tests are deeply integrated into your plugin and its + interaction with \QC. To add a test for something that requires + the infrastructure of \QC or your plugin to be set up, write a plugin + test. + + Plugin tests are executed by starting \QC with the \c{-test <pluginname>} + command line argument. \QC then fully loads your plugin and all the plugins + that it depends on, going through the normal \l{Plugin Life Cycle}. After + your plugin and all dependencies are fully initialized, your tests are + executed. Afterwards, \QC automatically closes. Therefore, your plugin + tests have access to all exported functionality of all \QC plugins that + your plugin depends on, like \c{Core::ICore}. Use QtTest's normal test + macros, like \c{QVERIFY} or \c{QCOMPARE} to report your test's success or + failure. + + To add plugin tests, add a QObject based class with private slots for your + tests, and register it with \l{ExtensionSystem::IPlugin::addTest()} in your + plugin's \l{ExtensionSystem::IPlugin::initialized()} method. Guard all test + related code with a check for \c{WITH_TESTS}, to avoid shipping a binary + release of your plugin with test functions. + + Include QtTest: + + \snippet exampleplugin/example.cpp test include + + Then implement the test functions: + + \snippet exampleplugin/example.cpp plugin tests + + Register your test in ExtensionSystem::IPlugin::initialize(): + + \snippet exampleplugin/example.cpp register tests + + If you declared the test object in the source file, like in this example, + also include the \c{.moc} file that is required for Qt's meta object + compiler: + + \snippet exampleplugin/example.cpp include moc + + \section1 Auto Tests + + To add a test that does not depend on a running \QC infrastructure, use an + auto test that lives independent of your plugin interface. Parsers are a + common example, but you can test many things in this way if they have been + written in a modular way. + + Even though your test does not live in your plugin interface, + like with plugin tests, you can still link the test to libraries and also + your plugin library itself, to avoid code duplication or duplicate + compilation of code. + + In principle you can use any auto test framework, + but QtTest is a simple one that integrates well with Qt, and is also used + for the \l{plugin tests}{Plugin Tests}. + + To add your test, add the test's C++ file, and use \c{add_qtc_test} in your + CMake file to add the test target. If your test uses your plugin library, + add it as a dependency with \c{DEPENDS}. + + In the following example, the plugin exports a function \c{addOne}: + + \quotefile exampleplugin/examplefunctions.h + + And implements it in a source file: + + \snippet exampleplugin/example.cpp exported function + + The test is linked against the plugin library target with \c{DEPENDS}: + + \snippet exampleplugin/CMakeLists.txt 6 + + The QtTest based test then includes the header from the plugin and + tests the function: + + \quotefile exampleplugin/tst_mytest.cpp +*/ |