Юнит-тестирование в Qt классов dll без Q_DECL_EXPORT
Есть разрабатываемая dll в Qt. Как организовать проект, чтоб добавить юнит-тесты для тестирования классов без Q_DECL_EXPORT?
Что пробовал я: Создавал проект с поддиректориями, где одна поддиректория - проект разрабатываемой dll, остальные поддиректории - проекты юнит-тестов. Окей, добавлял dll в юнит-тесты как "внешняя динамическая библиотека". Стали видны публичные классы (с Q_DECL_EXPORT). Далее добавляю пути к исходникам классов без экспорта: (test.pro SOURSEC += *путь/к/.срр/файлу/без_q_decl_export* HEADERT += *путь/к/.h/файлу/без_q_decl_export*) И все вроде бы должно работать. Но! Класс без экспортирования наследуется от класса С экспортированием. И Qt начинает пробрасывать ошибку redeclared without dllimport attribute: previous dllimport ignored. Что делать? Уважаемые программисты, работа встала!(
Ответы (1 шт):
Пример структуры проекта с статической библиотекой
Цель: организовать проект так, чтобы можно было тестировать классы без Q_DECL_EXPORT, избежав ошибки redeclared without dllimport attribute.
1. Общая структура папок
MyProject/
├── src/ # Исходники всей логики
│ ├── core/ # Классы, которые будут в статической библиотеке (включая классы без Q_DECL_EXPORT)
│ │ ├── myclass.cpp
│ │ ├── myclass.h
│ │ └── ...
│ └── dll/ # Классы с Q_DECL_EXPORT (публичный API DLL)
│ ├── dllclass.cpp
│ ├── dllclass.h
│ └── ...
├── lib/ # Статическая библиотека (будет собрана из src/core/)
│ └── MyStaticLib.pro # .pro-файл для сборки статической библиотеки
├── dll_project/ # Проект динамической библиотеки (DLL)
│ └── dll_project.pro # .pro-файл: подключает статическую библиотеку и публичный API
├── test_project/ # Проект юнит-тестов
│ └── test_project.pro # .pro-файл: подключает статическую библиотеку для тестов
└── MyProject.pro # Основной .pro-файл (объединяет подпроекты)
2. Настройка .pro-файлов
a) lib/MyStaticLib.pro (статическая библиотека)
TEMPLATE = lib
CONFIG += staticlib
HEADERS += ../src/core/myclass.h
SOURCES += ../src/core/myclass.cpp
# Указываем, что библиотека не экспортируется как DLL
DEFINES += QT_NODLL
b) dll_project/dll_project.pro (DLL-проект)
TEMPLATE = lib
CONFIG += dll
HEADERS += ../src/dll/dllclass.h
SOURCES += ../src/dll/dllclass.cpp
# Подключаем статическую библиотеку
LIBS += -L../lib -lMyStaticLib
# Экспортируем публичные классы
DEFINES += Q_DECL_EXPORT
c) test_project/test_project.pro (юнит-тесты)
TEMPLATE = app
CONFIG += console
HEADERS += ../src/core/myclass.h
SOURCES += test_myclass.cpp
# Подключаем статическую библиотеку
LIBS += -L../lib -lMyStaticLib
# Указываем пути к исходникам (если нужно)
INCLUDEPATH += ../src/core
d) MyProject.pro (основной .pro-файл)
TEMPLATE = subdirs
SUBDIRS += \
lib/MyStaticLib \
dll_project/dll_project \
test_project/test_project
3. Логика работы
- Статическая библиотека (
MyStaticLib) собирает все классы безQ_DECL_EXPORT(находятся вsrc/core/). Она компилируется в.a(или.libна Windows) и не экспортируется как DLL. - DLL-проект использует эту статическую библиотеку и добавляет публичные классы с
Q_DECL_EXPORT(вsrc/dll/). Эти классы формируют публичный API DLL. - Проект юнит-тестов также подключается к статической библиотеке, чтобы тестировать внутренние классы (без
Q_DECL_EXPORT).
4. Решение проблемы с dllimport
Ошибка redeclared without dllimport attribute возникает, когда класс без Q_DECL_EXPORT наследуется от класса с Q_DECL_EXPORT. Статическая библиотека решает эту проблему, так как:
- классы из статической библиотеки не экспортируются (нет
dllimport/dllexport); - DLL-проект и тесты линкуются со статической библиотекой, а не включают её классы напрямую.
5. Порядок сборки
- Сначала собирается
MyStaticLib(статическая библиотека). - Затем
dll_project(использует статическую библиотеку + свои экспортируемые классы). - Наконец,
test_project(тестирует классы из статической библиотеки).
6. Дополнительные нюансы
- Убедитесь, что пути в
INCLUDEPATHиLIBSкорректны (относительные пути зависят от структуры папок). - Если используете CMake, аналогичная логика реализуется через
add_library()иtarget_link_libraries(). - Для сложных иерархий наследования можно добавить условные директивы (
#ifdef/#ifndef) в заголовки классов.
Такая структура позволяет:
- тестировать внутренние классы без
Q_DECL_EXPORT; - избежать конфликтов экспорта/импорта;
- чётко разделить публичный и приватный API.