Глобально проверьте значение в макете - PullRequest
0 голосов
/ 01 октября 2018

У меня есть макет, представляющий оболочку API.

class MockApiWrapper : public ApiWrapper {
    public:
        MockNrfWrapper();
        virtual ~MockNrfWrapper();
        MOCK_METHOD1(api_do, void(int param));
};

Предположим, что api_do никогда не следует вызывать с param = 0.Поскольку я использую этот макет «везде», я хотел бы добавить утверждение / ожидание к каждому вызову, сделанному на api_do.Пример:

void MyClass::InvalidCallsToApi(void) {
    // api->api_do(0);  // Fails "global assert"
    // api->api_do(1);  // Fails by specific test
    api->api_do(2);     // Valid call
}
TEST(MyTestCase, FirstTest) {
    // Mock always checks that api_do is not called
    // with argument of 0
    EXPECT_CALL(api, api_do(Ne(1));
    uut->InvalidCallsToApi();
}

Я пытался сделать это с ON_CALL и Invoke в конструкторе, но либо он был переопределен добавленным EXPECT в тесте, либо я получил ошибку компиляции (не смогсделать ASSERT или EXPECT в вызываемом вызове).

Я надеюсь, что моя проблема ясна.Заранее спасибо за любой вклад!

1 Ответ

0 голосов
/ 01 октября 2018

Я придумал одно решение, это не самое хорошее, но приемлемое ИМО.

Код:

struct BInterface {
    virtual void foo(int) = 0;
};

struct BMock : public BInterface {
    MOCK_METHOD1(foo, void(int));
    BMock() {
        ON_CALL(*this, foo(0))
                .WillByDefault(::testing::InvokeWithoutArgs([](){ADD_FAILURE() << "This function can't be called with argument 0";}));
    }
};

void testedMethod(int a) {
    BInterface* myB = new BMock;
    myB->foo(a);
    delete myB;
}

TEST(myTest, okCase) {
    testedMethod(1);
}

TEST(myTest, notOkCase) {
    testedMethod(0);
}

Объяснение:
Мы добавляем действие по умолчанию к BMock, для каждого вызова метода foo с аргументом 0.
В этом действии мы вызываем лямбду, которая использует макрос GTest ADD_FAILURE() для генерациинефатальный сбой - эквивалент EXPECT_* макросов.Вместо этого вы можете использовать FAIL() для фатального сбоя, как в макросах ASSERT_*.

Мы используем макрос ON_CALL в конструкторе mock, что позволяет избежать вызова его с любым другим фиктивным объектом.

Ограничения:
Тот же трюк не будет работать, например, с EXPECT_CALL - я не знаю реализацию GMock, но я предполагаю, что EXPECT_CALL требует полностью инициализированный объект.

Вызов с соответствием, который принимает 0, все равно будет проходить (т. Е. EXPECT_CALL(myB, foo(::testing::_));, но так обстоит дело с любыми другими ожиданиями GMock. GMock всегда будет скрывать старые ожидания, когда встречаются новые. Вы должны создавать свои ожидания втаким образом, что они не будут переопределять предыдущие ожидания.
Добавление .RetiresOnSaturation() ко всем вашим EXPECT_CALL обеспечит переадресацию вызовов на действие по умолчанию (установлено ON_CALL), когда они неинтересны.
Пользовательские сопоставления будут полезны в случаях, когда есть несколько запрещенных значений.

MATCHER(IsValidApiArg, ""){return arg == 0 || arg == 1;}

ON_CALL(*this, api_foo(!IsValidApiArg)
        .WillByDefault(::testing::InvokeWithoutArgs([](){ADD_FAILURE();}));

EXPECT_CALL(myMock, api_foo(IsValidApiArg)); 

Примечание: Я все еще не могу поверить, что GMockне предоставляет действия по умолчанию для простого генерирования ошибки.Возможно, вы можете найти что-то более подходящее глубоко в документации.
Вы также можете создать собственное действие для этого, чтобы избежать всего этого Invoke и лямбд.

...