Модульное тестирование неэкспортированных классов в DLL

25

Мы разрабатываем приложение на C ++ с использованием Visual Studio 2008 и модульное тестирование с использованием Boost.Test. На данный момент у нас есть отдельное решение, которое содержит наши модульные тесты.

Многие из наших проектов в основном решении создают библиотеки DLL. Мы ограничены в тестовом покрытии, потому что мы не можем тестировать неэкспортированные классы.

У меня есть две идеи о том, как их можно проверить:

  1. Экспортировать все
  2. Поместите тесты в DLL (тот же проект и решение) и используйте внешний бегун Boost.Test

Я не совсем уверен, какими будут недостатки. Номер 1 выше нарушает инкапсуляцию на уровне модуля, а номер 2 может привести к гораздо большей DLL, если только в некоторых конфигурациях возможно включить тестовый код.

Итак, есть ли серьезные недостатки вышеупомянутых методов, или вы можете придумать другие решения?

    
задан Jon 31.03.2011 в 10:11
источник
  • Я хотел бы намекнуть на то, что CMake предлагает функцию «библиотеки объектов». (add_library (foo_obj OBJECT ...)) В моих проектах я создаю источники в библиотеках объектов, которые затем связываю в DLL (add_library (foo SHARED ... $ <TARGET_OBJECTS: foo_obj>)) и его тестовые драйверы ( add_executable (foo_test ... $ <TARGET_OBJECTS: foo_obj>)). Это вариант ответов ниже с использованием другой системы сборки (поэтому я добавил это как комментарий, а не ответ), но он решает ту же проблему. –  DevSolar 25.08.2015 в 14:47

4 ответа

13

Расширение ответа Тома Куарендона на этот вопрос , я использовал небольшой вариант ответа Саймона Стила:

  • Создайте тестовый проект (используя любую тестовую среду, которую вы любите, я использую CppUnit ).
  • В вашем test_case.cpp #include <header/in/source/project.h> .
  • В свойствах тестового проекта:
    • В Linker- > General добавьте $(IntDir) исходного проекта в каталоги дополнительных библиотек.
    • В Linker-> Input добавьте файлы .obj в Дополнительные зависимости.
  • Добавьте зависимость из тестового проекта в исходный проект в Project-> gt; Зависимости проекта.

Опять же, единственные накладные расходы по обслуживанию - стандартные для модульных тестов - для создания зависимости от модулей, которые вы хотите протестировать.

    
ответ дан Rai 17.06.2014 в 19:02
  • Еще одна деталь об этом подходе: если ваша тестируемая DLL использует предварительно скомпилированные заголовки, все файлы .obj, связанные с созданием тестируемой DLL, зависят от предварительно скомпилированного файла заголовка из тестируемой DLL. При создании тестового проекта это приводит к ошибке компоновщика LNK2011: предварительно скомпилированный объект не связан; изображение может не работать. В дополнение к конкретным объектным файлам, которые вы тестируете, вы должны добавить stdafx.obj (если ваш PCH-файл сгенерирован путем компиляции stdafx.cpp). –  Tim Crews 05.09.2015 в 23:27
  • Еще одно замечание по этому подходу: если в тестовом проекте используются несколько библиотек DLL, которые содержат идентично названные объектные файлы, которые необходимо протестировать, вы можете указать полный путь в настройках Linker-> Input, например. $ (SolutionDir) \ <имя_проекта> t \ $ (Платформа) \ $ (Конфигурация) \ <имя_объекта> .obj, чтобы различать их. –  Edward 12.05.2016 в 11:28
  • Вам также может понадобиться / FORCE: MULTIPLE, как я только что открыл. –  Rai 30.09.2016 в 14:23
3

Решение, которое я использую для этого, состоит в том, чтобы встроить такой же неэкспортированный код в мою тестовую DLL. Это увеличивает время сборки и означает добавление всего в оба проекта, но экономит на экспорте всего или помещает тесты в основной код продукта.

Другой возможностью было бы скомпилировать неэкспортированный код в библиотеку, которая используется как библиотекой DLL с экспортом, так и проектом модульного теста.

    
ответ дан Simon Steele 31.03.2011 в 10:16
  • Это может работать для небольших проектов, но у нас есть много кода, поэтому кошмар для обслуживания должен был бы внести изменения в два места. –  Jon 31.03.2011 в 10:24
  • Единственные изменения, которые необходимо выполнить, - это когда файлы добавляются или удаляются. Поэтому, если новый CPP-файл добавлен, содержащий код, который должен быть проверен модулем, тогда он должен быть добавлен к обоим проектам. Существует не две копии исходного кода, каждый исходный файл, содержащий тестируемый код, просто включен в оба проекта. –  Tom Quarendon 26.06.2013 в 11:22
  • Это был мой первый подход, который работал для меня. Я думал о потенциальной проблеме с ним, хотя иногда проект dll использует разные флаговые компиляции, чем тестовый проект. Поэтому проекты dll и test могут создавать разные объектные файлы для одного и того же исходного файла. Хотя в моем случае я был уверен, что они были такими же, в общем, безопаснее тестировать объектные файлы, создаваемые проектом dll, а не объектные файлы, создаваемые тестовым проектом. В итоге я переключил свой подход на описанный @Rai. –  Avi Tevet 05.10.2017 в 20:24
2

Также был поиск решения, возможно, будет легче поддерживать следующее.

Добавьте новую конфигурацию сборки, например, «Отладка модульного тестирования» в проекте DLL и измените тип конфигурации на «Static Library .lib» («Общие» -> «Тип конфигурации»).

Затем просто добавьте зависимость ваших модульных тестов от этого проекта, теперь все должно связываться вместе, когда вы используете новую конфигурацию сборки «Отладка модульного тестирования». Если вы используете сборки выпуска для модульных тестов, вам нужно добавить еще одну конфигурацию с оптимизацией выпуска.

Итак, преимущества этого решения:

  • низкая стоимость обслуживания
  • один проект DLL / статическая библиотека
  • не нужно вручную ссылаться на файлы .obj

Недостатки:

  • Дополнительные профили конфигурации потребуют некоторых изменений в вашей среде сборки (CI)
  • Увеличенное время компиляции

Обновление: Мы фактически использовали другой подход.

Мы добавили новые конфигурации «Test debug» / «Test release» для каждого имеющегося у нас проекта.

Для проектов .exe / .dll мы отключаем исходный файл main.cpp от компиляции и заменяем его на тот, который создает экземпляр инфраструктуры тестирования (например, gtest) и запускает все тесты, тесты находятся в отдельных файлах .cpp, которые также исключен из компиляции в обычных конфигурациях (Release / Debug) и включен только в тестовых конфигурациях.

Для проектов .lib у нас также есть новые конфигурации «Test debug» / «Test release», и мы преобразуем статическую библиотеку в файл .exe и предоставляем main.cpp, который создает среду тестирования и запускает тесты и сами себя проверяют. Связанные с тестом файлы исключаются из компиляции в конфигурациях выпуска / отладки.

    
ответ дан AndreiM 25.08.2015 в 14:40
0

Попробуйте создать такое определение, как следующее, где все файлы будут включать:

#define EXPORTTESTING __declspec(dllexport)

И используйте его вместо dllexport, вот так:

class EXPORTTESTING Foo 
{
 ...
};

Затем вы сможете отключить флаг для создания библиотеки DLL выпуска, но оставьте его включенным для DLL, тестируемой модулем.     

ответ дан Janik Zikovsky 17.01.2013 в 21:30
  • Не уверен, что это хороший способ сделать это ... Тестируемый код не должен быть изменен для тестирования. Даже если это простой макрос. –  toussa 02.01.2014 в 15:11