Использование GoogleMock для макета экземпляра, созданного тестируемым кодом - PullRequest
0 голосов
/ 13 сентября 2018

Я создал интерфейс (вот пример):

class DataStream
{
    virtual std::string read(std::string terminator) = 0;
    virtual size_t write(std::string data) = 0;
};

, для которого существует конкретная реализация, например:

class SerialDataStream : public DataStream
{
public:
    // NOTE: This constructor will throw an exception if the
    // serial port cannot be opened.
    SerialDataStream(string port, int baudrate);
    std::string read(std::string terminator);
    size_t write(std::string data);
}

И интерфейс используется дляпример:

class SomeThing
{
public:
    SomeThing(std::shared_ptr<DataStream> stream);
}

Используя GoogleMock, тестирование класса SomeThing довольно простое, все, что вам нужно сделать, это создать фиктивную реализацию для интерфейса, например:

class MockDataStream : public DataStream
{
public:
    MOCK_METHOD1(read, size_t(std::vector<uint8_t>&));
    MOCK_METHOD1(write, size_t(std::vector<uint8_t>&));
}

Гдетест выглядел бы примерно так:

std::shared_ptr<MockDataStream> mock_stream(nullptr);
mock_stream = std::make_shared<MockDataStream>();
EXPECT_CALL(*mock_stream, write("START")).Times(AtLeast(1));
EXPECT_CALL(*mock_stream, read("\n")).Times(AtLeast(1));
SomeThing some_thing = SomeThing(mock_stream);

Это довольно круто, так как легко позволяет мне протестировать, как класс SomeThing использует интерфейс DataStream.

Однако также существует некоторый кодчья работа заключается в создании новых (конкретных) объектов DataStream, и я обнаружил, что с помощью GoogleMock это немного сложнее проверить.Например, вот фрагмент кода, который нужно протестировать:

std::shared_ptr<DataStream> datastream(nullptr);

// Try and open the serial port:
try
{
    std::shared_ptr<SerialDataStream> serialstream =
        std::make_shared<SerialDataStream>("/dev/tty99", 115200);
}
catch (...) 
{
    // Returns a nullptr
    return datastream;
}

// Check if there is a known device on the other end:
datastream = std::static_pointer_cast<DataStream>(serialstream);
if (!device_is_connected(datastream))
{
    datastream = nullptr;
}
return datastream;

Я изо всех сил пытаюсь найти эффективный метод для тестирования этого кода с помощью GoogleMock:

  1. Я хотел бы посмеяться над конструктором (SerialDataStream), чтобы он выдавал исключение, и путь ошибки выполнялся, как и ожидалось.
  2. Я хотел бы проверить успешный путь, где частный API "device_is_connected" использует недавно созданныйобъект datastream.

Это тот случай, когда у меня нет другого выбора, кроме как создать ложную реализацию SerialDataStream и использовать внедрение зависимостей для проверки кода, который создает конкретные объекты DataStream?

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

datastream.write("DISCOVER");
string response = datastream.read("\n");
if (discovery_ok(response))
{ 
    // do stuff
}

Я почти уверен, что ответил на свой вопрос и у меня не будет другого выбора, кроме как подделать класс SerialDataStream и использовать зависимостьХорошая инъекция, сделайте частные API публичными и просто протестируйте их через GoogleMock, но я открыт для предложений, если есть лучший способ, которым я мог бы / должен делать что-то здесь.

1 Ответ

0 голосов
/ 13 сентября 2018

Глядя на предоставленный вами код, я обнаружил, что спрашиваю:
Что именно должна делать функция?Похоже, что
(1) создает SerialDataStream и
(2) проверяет, подключено ли устройство.

Вы можете разбить функцию на две (индивидуально проверяемые) части.
Тем не менее остается вопрос о том, как обращаться с std::shared_ptr<SerialDataStream> serialstream = std::make_shared<SerialDataStream>("/dev/tty99", 115200); - в какой-то момент вам нужно будет заняться (скрытым) новым там.

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

template<typename T>
std::shared_ptr<DataStream> createDatastream()
{
    std::shared_ptr<DataStream> datastream(nullptr);
    std::shared_ptr<T> datastream = std::make_shared<T>("/dev/tty99", 115200);
    return datastream;
}

И затем создать экземпляр класса / функции с помощью SerialDataStream в вашем приложении, используяMockDataStream для проверки функции (й).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...