Тестирование класса, которое зависит от статических функций другого класса

StackOverflow https://stackoverflow.com/questions/4073836

Вопрос

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

Все работало нормально, пока не попробую тестировать свой класс.

Вот простой код примера проблемы:

class A {
    static String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B {
    B() {}
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return A::getSomething();
        } else {
            return "Invalid name!";
        }
    }
}

Предполагая, что класс A работает правильно (и проверено его модульными тестами), я хотел бы проверить Ворон Функция в классе B.

Мой первый вариант будет создавать издевательства для внутренних классов (в этом образец - класс а), но в этом случае это даст мне нечего наследовать из А, потому что он имеет только статические функции.

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

Мой вопрос вам: Есть в лучших способах протестировать классы C ++, что зависит от статических классов / функций, чем мои текущие варианты?

Заранее спасибо,

Озвучивать

Это было полезно?

Решение

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

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

Другие советы

Если вы не используете монолитный тестовый набор, то его легко. Я предполагаю, что у вас есть класс A в A.CPP и класс B в B.CPP, а тесты B находятся в b_test.cpp.

Создайте файл под названием a_mock.cpp

class A
{
    static String getSometing() {
        return String("Expected Something");
    }
};

Затем при компиляции файла B_TEST просто свяжите с A a_mock.o, а не AO.

g++ -Wall B_test.cpp B.cpp A_mock.cpp

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

Почему статическая функция? Я бы предложил не сделать его статическим.

Затем вы можете создать интерфейс для класса A (в C ++ это означает класс с только чистыми виртуальными заголовками функций) с именем ainterface. Класс A будет реализовывать (наследующую) ainterface и реализовать эти виртуальные функции.

Затем пройдите указатель на этот интерфейс в конструктор класса B и храните его в переменме участника с именем M_A. Затем в вашем тесте создайте MockClassa, который реализует ainterface. Пройдите MockClassa в конструктор класса B и установите M_a на вход.

class AInterface
{
   virtual String getSomething() = 0;
}

class A : public AInterface
{
    String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B 
{
    B(AInterface A) :  { m_A = A; }
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return m_A.getSomething();
        } else {
            return "Invalid name!";
        }
    }
    AInterface m_A;
}

Тестовый код

class MockClassA : public AInterface
{
    String getSometing() {
        return String("Whatever I want. This is a test");
    }
}   

void test ()
{
   // "MockClassA" would just be "A" in regular code
   auto instanceOfB = B(MockClassA());

   String returnValue = instanceOfB.runSomething("something");
   :
   :
}

Я бы сказал: «Человек, некоторые люди слишком далеко берут модуль!»

Просто проверьте два класса как единое целое. Класс A в любом случае записан в класс B.

Вы должны взять класс через шаблон и явно экспортировать это мнение (B<A>) Чтобы избежать проблем линкера, если он не был ранее встроенным в соответствии с опубликованными. Таким образом, вы можете вставить другие классы для целей тестирования, как вам требуется, и в любом случае - это хорошая практика. Мне также интересно, почему ваш пример выглядит так сильно, как Java - я должен был прочитать его примерно в пять раз, прежде чем определить, что это на самом деле было C ++ как указано.

template<typename T> class BImpl {
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return T::getSomething();
        } else {
            return "Invalid name!";
        }
    }
};
typedef BImpl<A> B; // Just plugs in to existing code.

Теперь вы можете заменить макетный класс для A, хотя вы не можете унаследовать от него. Infact, это также расширяется другим способом - Crtp.

class A : public BImpl<A> {
    String getSomething() {
        // Now it's non-static! IT'S A MIRACLE!
    }
}

Чудеса шаблонов никогда не перестают удивлять меня.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top