Можно ли протестировать "внутренний" класс из dll c ++ с помощью MSTest? - PullRequest
7 голосов
/ 14 октября 2011

В настоящее время мы пытаемся добавить модульное тестирование в наше приложение c ++. Приложение состоит из 30 проектов, которые генерируют 29 DLL и 1 EXE. Мы используем MSTest для запуска нашего модульного теста, так как он уже включен в Visual Studio 2010.

Отлично работает для класса, который объявлен "публичным". Эти классы имеют это в начале:

#ifdef RESEAU_IMPL
    #define CLASS_DECL      _declspec(dllexport)
#else
    #define CLASS_DECL      _declspec(dllimport)
#endif 

Но для всех остальных классов (90% кода) они не объявлены публичными, поэтому мы не можем использовать их в нашем тесте.

Я читал в Google об атрибуте InternalVisibleTo, но, похоже, он работает только со сборкой c # .NET. Я прав? Я также читаю, чтобы объявить мой класс "as_friend", но я не уверен, где это поставить.

Итак, вкратце: я хочу протестировать класс, который не экспортируется / не публикуется в DLL. Как мне это сделать?

Спасибо

* РЕДАКТИРОВАТЬ *

Гишу заметил, что модульное тестирование невозможно в неуправляемом коде, но возможно. Видите, это TestMethode, который тестирует собственный код C ++. CVersion находится в C ++ MFC.

[TestMethod]
void AssignationCVersion()
{
    CVersion version1234(1,2,3,4);
    CVersion version4321(4,3,2,1);
    Assert::IsTrue(version1234 != version4321);
    version1234 = version4321;
    Assert::IsTrue(version1234 == version4321);
};

Но то, что кажется невозможным, - это использовать специальный тег для проверки внутренней функции. Я первый согласен с тем, что тестирование внутреннего метода не является хорошей практикой, но эти DLL не являются служебными функциями, а являются частью "настоящего" приложения. (возможно, это плохой дизайн, но это было сделано 15 лет назад). У кого-нибудь есть идеи на эту тему?

Ответы [ 3 ]

7 голосов
/ 26 июня 2013

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

Три варианта выглядят так:

  • Поместите тестовый код в DLL, чтобы он имел доступ к неэкспортированным классам и функциям
  • Добавьте все файлы, содержащие тестовый код, в тестовый проект, чтобы они компилировались дважды (не знаю, относится ли это к MSTEst, но было бы так, как вы бы это делали, используя Boost-тест или CPPunit)
  • Сборка всего неэкспортируемого тестируемого кода в статическую библиотеку, которая затем связывается с тестовым кодом и DLL.

Все они имеют разные проблемы.

Поместить тестовый код в DLL не идеально. Либо вы включаете его только в непроизводственные сборки, в этом случае вы не тестируете то, что выпускаете, либо вы включаете все сборки, в этом случае вы отправляете тестовый код, что может быть нежелательно. Кроме того, тогда должна быть какая-то точка входа для доступа к этим тестам, что вынуждает компилятор включать весь код, не позволяя оптимизатору удалить его, если в противном случае он будет считаться недоступным (возможно, часть кода если к тестированию нельзя получить доступ ни из одного из общедоступных методов в DLL, поэтому оптимизатор может решить удалить их как мертвый код - наличие тестов в DLL предотвращает это).

Добавление исходных файлов в оба проекта увеличивает время сборки и сложность обслуживания. Каждый раз, когда вы добавляете новый исходный файл или удаляете исходный файл, его нужно будет добавить в обоих местах. Также, в зависимости от размера кода, это может значительно увеличить время сборки, так как приходится много кода создавать вдвое.

Помещение всего неэкспортируемого тестируемого кода в статическую библиотеку имеет недостаток, заключающийся в создании дополнительного проекта в решении, и усложняет организацию. Вы должны быть осторожны со структурой кода (например, один исходный файл должен содержать только экспортированный или неэкспортированный код), а это означает, что вам нужны отдельные тестовые проекты для экспортированных частей и для неэкспортированных частей. Однако это означает, что код компилируется только один раз, и тесты не являются частью окончательного исполняемого файла, и оптимизатор может выполнять свою полную работу.

В зависимости от размера открытого интерфейса с DLL, то есть количества экспортируемых классов / функций, на мой взгляд, наиболее подходящим является третий вариант. Часто у вас есть только небольшой публичный интерфейс, который является фасадом для большей внутренней структуры. Все, кроме общедоступного фасада, может быть помещено в отдельную статическую библиотеку, которую затем можно легко связать с исполняемым файлом теста и с DLL.

6 голосов
/ 14 октября 2011

Нет способа, независимо от того, являетесь ли вы модульным модулем тестирования или чем-то еще, тестировать код, который вы не видите. DLL в Windows экспортирует только те символы, для которых определено __declspec(dllexport). Любой другой символ обрабатывается как внутренний , когда DLL компилируется , и не будет виден коду, использующему DLL.

Это важно, потому что это означает, что компоновщик может оптимизировать, изменять или удалять код, который не экспортируется. Код, который вы хотите проверить , может вообще отсутствовать . Это может быть там, но в другой форме, чем вы ожидаете. DLL компилируется по контракту, что все, что объявлено с dllexport, должно присутствовать и быть видимым, а все остальное должно работать. Он не должен быть доступен из внешнего мира.

Это не недостаток MSTest (хотя он имеет множество других недостатков и является довольно ужасным выбором для модульного тестирования кода C ++)

Если вы хотите проверить этот код, у вас есть два варианта:

  • экспортируйте его с dllexport или
  • написать свой код модульного теста как часть самой dll.
0 голосов
/ 14 октября 2011

Ну, не стреляйте в курьера.

  • Модульное тестирование Visual Studio (также называемое тестами, запускаемыми с помощью MSTest.exe) только тестирование управляемого кода . Вы не можете протестировать неуправляемый C ++. В VS11 (новая версия) поставляется новая встроенная среда модульного тестирования.
  • InternalsVisible, как вы сказали, также относится только к управляемому коду.
  • ИМХО Обычно не нужно тестировать внутренние классы . Как и частные типы или методы, вы проверяете их с помощью открытых / открытых методов, которые их используют. Так что если PublicA.Method1 () - это способ, которым ваши клиенты будут использовать InternalHelper.Method2 (); затем я полагаюсь на тест PublicA.Method1 (), чтобы сообщить мне, не нарушен ли какой-либо из них.
  • Если вам необходимо протестировать внутренние классы, попробуйте сделать их общедоступными (если методы достаточно сложны .. см. Рефакторинг MethodObject). Или вы можете сделать тестовые классы другом внутренних классов.

.

class ProductionSUT
{
  // production code to be tested
  friend class TestProductSUT;
}

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

...