Google Mock: нет подходящего конструктора по умолчанию?

Использование Visual Studio 2010 C++ с googlemock. Я пытаюсь использовать макет, который я создал, и я получаю ошибку компилятора в строке:

EmployeeFake employeeStub;

Ошибка:

1>c:\someclasstests.cpp(22): error C2512: 'MyNamespace::EmployeeFake' : no appropriate
default constructor available

Поддельный Сотрудник:

class EmployeeFake: public Employee{
 public:
  MOCK_CONST_METHOD0(GetSalary,
      double());
}

Работник:

class Employee 
{
public:
    Employee(PensionPlan *pensionPlan, const char * fullName);
    virtual ~Employee(void);

    virtual double GetSalary() const;
}

Я понимаю, что проблема в том, что базовый класс не имеет конструктора по умолчанию, но как мне это исправить? Нужно ли мне добавлять конструктор по умолчанию в мой базовый класс? Или мне нужно добавить конструктор в мой фиктивный класс? Или что-то другое?


person User    schedule 05.07.2011    source источник
comment
Вы можете добавить конструктор в производный класс, который вызывает базовый конструктор с подходящими параметрами, или вы можете указать значения по умолчанию для конструктора базового класса...   -  person Kerrek SB    schedule 06.07.2011
comment
@Kerrek SB: Почему бы не опубликовать это как ответ, раз уж это так?   -  person Eric Fortin    schedule 06.07.2011


Ответы (2)


Вы можете просто добавить конструктор в свой макет, который делегирует конструктору Employee:

 class MockEmployee : public Employee {
     public:
         MockEmployee(PensionPlan* pension_plan, const char* full_name)
         : Employee(pension_plan, full_name) {}
         // ...
 };

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

  1. Сделать сотрудника чисто виртуальным (с защищенным конструктором по умолчанию).
  2. Переименуйте текущую реализацию Employee в имя, описывающее тип сотрудника (например, FullTimeEmployee или EmployeeWithPensionPlan), и сделайте его наследником чисто виртуального типа.
  3. Используйте "virtual ~Employee()" вместо "virtual ~Employee(void)" (явное использование void в параметре является переносом из C и, насколько мне известно, не в моде в большинстве сообществ C++).
  4. Используйте для имени «const string&» вместо «const char*».

Итак, чтобы уточнить, моя рекомендация будет:

class Employee {
    public:
        virtual ~Employee() {}
        virtual double GetSalary() const = 0;
    protected:
        Employee() {}
};

class FullTimeEmployee : public Employee {
     // your concrete implementation goes here
};

class MockEmployee : public Employee {
    public:
        MockEmployee() {}
        virtual ~MockEmployee() {}
        // ... your mock method goes here ...
};
person Michael Aaron Safyan    schedule 06.07.2011
comment
Думаю, я не уверен, что хочу иметь дело с передачей объекта PensionPlan. Я бы подумал, что для имитации GetSalary() мне действительно не нужен объект пенсионного плана, потому что я просто возвращал бы готовый ответ от GetSalary(). - person User; 06.07.2011
comment
@User, да, именно поэтому я рекомендую использовать чистый виртуальный базовый класс Employee без связанных данных. Его конструктор вообще не требует никаких параметров, так как он не должен выполнять инициализацию. Затем сделайте конкретную реализацию, а также макет этого интерфейса. - person Michael Aaron Safyan; 06.07.2011
comment
Понятно, я читаю code.google.com/p/googlemock/wiki / что, кажется, предполагает то же самое. Часть меня задается вопросом о быстром и грязном решении добавления конструктора по умолчанию в мой класс Employee. Что вы думаете? - person User; 06.07.2011
comment
Итак, в сценарии модульного тестирования означает ли это, что почти каждый класс, который вы хотите протестировать, нуждается в классе интерфейса и классе реализации (фактически удваивая количество классов)? - person User; 08.07.2011

Вы уже предложили возможный ответ, но давайте уточним некоторые варианты:

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

explicit Employee(PensionPlan *pensionPlan = 0, const char * fullName = "");

(Обратите внимание, что мы говорим explicit, чтобы избежать неявных преобразований из PensionPlan*.)

2) Вызов конструктора в базовом списке инициализаторов конструктора производного класса:

EmployeeFake::EmployeFake() : Employee(0, "") { }

2a) Дайте EmployeeFake соответствующий конструктор и передайте его дальше:

EmployeeFake::EmployeeFake(PensionPlan *p) : Employee(p, "[fake]") { } 

(Обратите внимание, что (1) — это объявление, а (2) и (2а) — определения.)

person Kerrek SB    schedule 06.07.2011
comment
Я настоятельно рекомендую против № 1; в C++ параметров по умолчанию действительно следует избегать как чумы. # 2 является вариантом только в том случае, если это не нарушает инварианты Employee. Кто сказал, что передача NULL не взорвет его? - person Michael Aaron Safyan; 06.07.2011
comment
@Michael: Если вы пишете класс, который можно взорвать, вызвав его конструктор, то взрыв, безусловно, возможен! :-) А что у вас с аргументами по умолчанию? - person Kerrek SB; 06.07.2011
comment
Это не относится к конструкторам, но аргументы по умолчанию могут иметь неожиданные последствия при попытке получить адрес функции (эй, она работала с таким количеством параметров этих типов, почему я не могу взять ее адрес под этой сигнатурой?). Так что лучше вообще избегать (и, соответственно, конструкторов). Также... - person Michael Aaron Safyan; 06.07.2011
comment
... параметры по умолчанию плохи, особенно в больших проектах, если значения по умолчанию меняются и не все единицы компиляции перекомпилируются, поскольку все, что не было перекомпилировано, не увидит новых значений и может привести к несоответствиям. Принудительное использование значений по умолчанию в определении, а не в подписи, может избежать подобных проблем. - person Michael Aaron Safyan; 06.07.2011
comment
@Michael: Да, значения по умолчанию должны всегда быть в определении. Что ж, я понимаю вашу точку зрения, но просто представьте себе стандартную библиотеку без аргументов по умолчанию — сколько явных хеш-функторов вы хотите создать? Я согласен с тем, что должны быть рекомендации по дизайну, но я бы не был таким абсолютистом, как вы предлагаете... :-) - person Kerrek SB; 06.07.2011
comment
Я не имел в виду, что они никогда не являются хорошей идеей (да, есть места, где они полезны), но я бы сказал, что их следует избегать в большинстве случаев (включая этот). - person Michael Aaron Safyan; 06.07.2011
comment
Это странно, потому что ни один из примеров googlemock, с которыми я сталкивался, не показывает конструктор в фиктивном объекте. Кажется, что в идеале не нужно было бы создавать объект PensionPlan только для имитации вызова GetSalary(). Но я думаю, похоже, что мне просто нужно успокоить компилятор, передав что-то в конструктор сотрудников? - person User; 06.07.2011
comment
@User: Ну, да, или измените конструктор Employee, если вам все равно никогда не понадобятся эти значения! - person Kerrek SB; 06.07.2011
comment
@Kerrek SB: Ну, мне они не нужны в версии-заглушке / макете, но в реальной версии они нужны. - person User; 06.07.2011