Как получить доступ к полям частного класса из теста, используя UnitTest ++? - PullRequest
13 голосов
/ 18 января 2010

При кодировании моих модульных тестов с использованием UnitTest ++ я сталкиваюсь с неприятностью. Мне интересно, как получить доступ к закрытым полям класса члена чистым способом (или, может быть, любым способом ...)

К настоящему времени у меня есть решение для доступа к защищенным членам , используя фикстуру класса, полученную из тестируемого класса. Следующий код показывает идею:

struct MyFixture : ClassUnderTest { };

TEST_FIXTURE(MyFixture, OneTest)
{
    do_something();
    CHECK(protected_field == true);
}

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

Я пытался объявить тестовые классы как друзей , но так как они созданы каким-то особым образом UnitTest ++, мне пока не удалось это сделать.

Кто-нибудь знает, как подружить тестовые классы с тестируемыми классами?

Есть ли другой способ решения этой проблемы более простым или иным способом?

Спасибо всем заранее.

Ответы [ 6 ]

13 голосов
/ 18 января 2010

Есть один действительно уродливый, но удивительно полезный хак, который я обычно использую для модульного тестирования:

#define private public
#define protected public

#include "class_to_be_tested.h"

// and here, all class's methods and fields are public :P

НЕ используйте его для чего-либо другого, кроме модульного тестирования!

Кроме того, этот метод имеет некоторые ограничения - во-первых, не все частное имеет префикс private.Во-вторых, один популярный компилятор преобразует спецификатор доступа в символ компоновщика.

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

class my_class
{
private:
   char name[40];
   char grade;
   int age;
public:
   //
}

struct my_class_hack
{
public:
   char name[40];
   char grade;
   int age;

}

struct hack_it* my_class_hacked = (my_class_hack*)ptr;

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

4 голосов
/ 18 января 2010

Я бы тоже пошел на #define Hack,

, но также поместите структуру класса #define, чтобы сделать данные неявных закрытых классов публичными.

Я должен не согласиться с Дмитрием:

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

2.) Снова, как показано в 1.) -> хорошая идея, если эти интерфейсы действительно доступны только для тестирования

3.) Работает только в том случае, если доступ защищен, что также компрометирует инкапсуляцию

4.) Также означает изменение моего производственного кода только для тестирования и даже создает связь между рабочим кодом и моим тестовым кодом !!!

5.) С вашей точки зрения это правильно, у меня скорее уродливый тестовый код, чем уродливый рабочий код:)

4 голосов
/ 18 января 2010

Модульное тестирование - это тестирование ваших объектов через открытый интерфейс.Тот факт, что это может быть сложно, объясняет, почему написание тестируемого кода иногда называют art .Не каждый может написать тестируемый код сразу, поэтому люди впервые придумали XP-подход к написанию тестов.Звучит нереально, работает в реальности.

Однако, если вам абсолютно необходимо для тестирования частных функций, вот список методов, которые я бы рассмотрел в порядке моих собственных предпочтений:

  1. Доступ к закрытым переменным-членам осуществляется через общедоступные сеттеры и геттеры.

  2. Я бы порекомендовал сделать вашу частную функцию-члена нестатическойфункция-член в пространстве имен, которая может быть вызвана, например, details или internal.Не объявляйте это в заголовочном файле, просто определите его в том же файле, где определены ваши функции класса.Добавьте его объявление в заголовочный файл myClass_internal.h в проекте модульного тестирования и протестируйте его.Сложности связаны в значительной степени со сложностью вашей архитектуры.

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

  4. Сделайте ваш тест другом вашего класса для тестирования.Сложность зависит от используемой вами тестовой среды.Скажем, с gtest , который я использую, это довольно сложно:)

  5. Хак с переопределением public и private должен быть вашим абсолютно последним курортомесли все остальное терпит неудачу. Хотя я бы предпочел вместо этого изменить дизайн на более тестируемый.

4 голосов
/ 18 января 2010

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

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

1 голос
/ 19 июля 2011

Хотя я согласен со всеми, кто говорит «не делай », иногда вы работаете в большом проекте, где у вас нет свободы вносить все изменения в тестируемый класс тебе бы хотелось. В таких ситуациях я бы предпочел декларацию друга взлому публичного / частного переопределения.

Я понял, как это сделать для UnitTest ++ Вот пример для класса "Deck" с защищенным / приватным методом "dealInternal", который вы хотели бы использовать из теста "Internals".

    //------------- Deck.h -------------
    namespace SuiteDeck { class TestInternals; }

    class Deck {
        // etc...
        private:
        friend class SuiteDeck::TestInternals;
        bool dealInternal();
    };

    //------------ TestDeck.cpp ----------
    #include "UnitTest++.h"
    #include "Deck.h"

    SUITE(Deck) {
        TEST(Internals) {
            Deck d;
            CHECK(d.dealInternal() == true); // or whatever
        }
    }
0 голосов
/ 18 января 2010

Лучше избегать тестирования личных вещей.Почему вы хотите протестировать private_field?Что становится неправильным, если для private_field задано недопустимое значение?Можно ли проверить это неправильное поведение, вместо того чтобы утверждать, что значение неверное?

Другие опции включают

  • играть с препроцессором, чтобы сделать его общедоступным, когда код скомпилирован для модульного тестирования

  • extractприватное поле и связанная логика в новый класс, где он будет общедоступным, и ClassUnderTest будет полагаться на этот новый класс.

...