Мне нужно издеваться над элементами массива в макете черепахи. К сожалению, поскольку макрокоманда черепахи MOCK_BASE_CLASS добавляет лишний "мусор", например mock::object, mock::detail::base<> и т. Д., Размеры базового и фиктивного типов больше не идентичны. Следовательно, индексирование указателя не выполняется, когда указатель на базовый класс указывает на массив фиктивного типа, как показано ниже.

#define BOOST_TEST_MODULE First_TestSuite
#include <boost/test/included/unit_test.hpp>
#include <turtle/mock.hpp>
#include <iostream>

struct Foo
{
    int data = 42;
};

MOCK_BASE_CLASS(mockFoo , Foo)
{
};

BOOST_AUTO_TEST_CASE( Demo )
{
    mockFoo mf[10];
    Foo* b = mf;

    std::cout << "b[1].data = " << b[1].data << std::endl;
    BOOST_CHECK(b[1].data == 42);
    //BOOST_CHECK(sizeof(mockFoo) == sizeof(Foo));  // Also fails. This is the culprit
}

Результат выполнения

Running 1 test case...
b[1].data = 32764
Test047b.cpp(23): error: in "Demo": check b[1].data == 42 has failed

*** 1 failure is detected in the test module "First_TestSuite"

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


Обновить

Следующий пример ближе к проблеме, с которой я столкнулся. Использование MOCK_BASE_CLASS практически неизбежно для имитации виртуальной функции. Мне известно, что проблема заключается в хранении массива из mockFoo как Foo. Я нашел решение, которое опубликую, если вы его оставите.

struct Foo
{
    int value = 42;
    int func(){ return value;}
    virtual void unused(){}
};

MOCK_BASE_CLASS(mockFoo , Foo)
{
    MOCK_METHOD(unused , 0 , void());
};

struct Bar
{
    Bar(Foo* f) : foo(f)
    {
    }

    Foo* foo;
};

BOOST_AUTO_TEST_CASE( Demo )
{
    mockFoo mf[10];
    Bar bar(mf); // uh-oh! 

    int value = bar.foo[1].func();
    std::cout << "bar.foo[1].func() = " << value << std::endl;
    BOOST_CHECK(42 == value);
}

Результат

Running 1 test case...
bar.foo[1].func() = -960497840
Test047d.cpp(35): error: in "Demo": check 42 == value has failed

*** 1 failure is detected in the test module "First_TestSuite"
0
Olumide 12 Мар 2021 в 21:08

2 ответа

Лучший ответ

Мои подозрения подтвердились: указатель на базовый класс нельзя использовать для арифметики указателей, когда массив содержит объекты типа производного класса (из стандарта - source). Соответственно, я создал оболочку / адаптер, который я называю индексатором, чтобы управлять индексированием правильного типа, как показано ниже.

struct Foo
{
    int value = 42;
    int func(){ return value;}
    virtual void unused(){}
};

MOCK_BASE_CLASS(mockFoo , Foo)
{
    MOCK_METHOD(unused , 0 , void());
};

class FooIndexer
{
public:
    typedef Foo& (FooIndexer::*IndexerFunc)(int index);

    template<typename T>
    FooIndexer(T (&array)[10])
        : foo(array)
        , indexerFunc( &FooIndexer::indexer<T> )
    {
    }

    template<typename T>
    Foo& indexer(int index)
    {
        T* array = static_cast<T*>(foo);
        return array[index];
    }
    
    Foo& operator[](int index)
    {
        return (this->*(indexerFunc))(index);
    }

private:
    Foo* foo;
    IndexerFunc indexerFunc = nullptr;
};

struct Bar
{
    template<typename T>
    Bar(T (&f)[10]) : foo(f)
    {
    }

    FooIndexer foo;
};

BOOST_AUTO_TEST_CASE( Demo )
{
    mockFoo mf[10];
    Bar bar(mf);

    int value = bar.foo[1].func();
    std::cout << "bar.foo[1].func() = " << value << std::endl;
    BOOST_CHECK(42 == value);
}

Результат выполнения

Running 1 test case...
bar.foo[1].func() = 42

*** No errors detected
0
Olumide 15 Мар 2021 в 00:40

Если вы посмотрите на раздел создания в документации и прокрутите немного ниже вы увидите, что использование макроса MOCK_BASE_CLASS не является обязательным, первая альтернатива - наследование от mock::object вручную (на самом деле это то, что макрос делает под капотом).

И даже это несложное требование:

Наследование от mock :: object необязательно, но дает следующие дополнительные преимущества:

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

Поэтому, в конце концов, вы можете просто сделать

struct mockFoo : Foo
{
};

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

1
mat007 13 Мар 2021 в 09:08