Перенаправить реальную функцию на издевательскую функцию с помощью Gmock?

Когда я исследовал Gmock от Google, я установил и построил проект, работающий до сих пор. Но у меня есть некоторые опасения по поводу насмешки над функцией. Теперь у меня есть следующие файлы:

‹1> myGtest.h

#ifndef MYGTEST_H_
#define MYGTEST_H_

int test(int);
int function(int);

#endif /* MYGTEST_H_ */

‹2> src_code.cpp

#include <stdio.h>
#include "myGtest.h"

int test(int a) {
    printf("NOT overridden!\n");
    return a;
}

int function(int a){

    int x = test(a);
    if(x == 0)
        return 99;
    else
        return 0;
}

‹3> myGtest_dummy.h

#ifndef MYGTEST_DUMMY_H_
#define MYGTEST_DUMMY_H_

#include "gmock/gmock.h"
#include "../myGtest/myGtest.h"

class myGtestMock
{
public:
    myGtestMock(){};
    ~myGtestMock(){};
    MOCK_METHOD1(test, int(int));
};

#endif /* MYGTEST_DUMMY_H_ */

‹4> test_program.cpp

#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/myGtest/myGtest.h"
#include "src/dummy/myGtest_dummy.h"

using testing::_;
using testing::Return;
using testing::InSequence;
using ::testing::AtLeast;

extern int function(int a);
extern int test(int a);

class BTest:public testing::Test{
public:
    myGtestMock mock_test;


    int __wrap_test(int a);
};

int BTest::__wrap_test(int a){

    printf("overridden!\n");
    return a;
}

TEST_F(BTest, CallMockTest) {

    EXPECT_CALL(mock_test, test(0))
            .WillOnce(Invoke(this, &BTest::__wrap_test));

    function(99);
}

int main(int argc, char *argv[]) {

    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Не могли бы вы помочь мне объяснить это: как я могу издеваться над функцией int test(int)? Я хотел бы ожидать, что после выполнения TEST_F(BTest, CallMockTest) вызов программы function(99);. Тогда вместо int test(int) будет вызываться моя издевательская функция int __wrap_test(int).

Большое спасибо за ответ.


person Thảo M. Hoàng    schedule 06.04.2016    source источник


Ответы (1)


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

С вашей текущей реализацией, когда вы вызываете функцию(), она по-прежнему будет вызывать реальную реализацию test(). Таким образом, ваш EXPECT_CALL всегда будет терпеть неудачу. В идеале test() должен быть частью интерфейса, который вы хотите имитировать. Сказать:

class MyInterface{
public:
    ...
    virtual int test(int a) = 0;    
};

Примечание: для простоты я не буду рассматривать реальную реализацию MyInterface, так как в любом случае мы намерены обойти реальную реализацию, используя наш макет.

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

class MyClass{
    ....
    int function(int a, MyInterface * interface){
        int x = interface->test(a);
        if(x == 0)
            return 99;
        else
            return 0;   
    }
};

Теперь, используя второй параметр функции(), вы можете передать созданный вами макет. Но перед этим вам нужно определить, что вы издеваетесь над MyInterface:

class myGtestMock : public MyInterface{
    ....
};

Теперь в вашем тестовом файле:

TEST_F(BTest, CallMockTest) {
    myGtestMock myMockObj;

    //Testing (x==0)
    EXPECT_CALL(myMockObj, test(99))   
    .Times(1)
    .WillOnce(Return(0));       //set the action

    EXPECT_EQ(99,function(99, &myMockObj));
    Mock::VerifyAndClear(&myMockObj); //to clear the expectation

    //testing else
    EXPECT_CALL(myMockObj, test(99))   
    .Times(1)
    .WillOnce(Return(1));       //set the action

    EXPECT_EQ(0,function(99, &myMockObj));
    Mock::VerifyAndClear(&myMockObj); //to clear the expectation
}  

Есть много хитростей, которые вы можете узнать из google mock CookBook

person MIbrah    schedule 06.04.2016
comment
Большое спасибо за ваше время, чтобы ответить мне. - person Thảo M. Hoàng; 07.04.2016
comment
Я понял, что ты сказал. Но мой клиент дал мне только исходный код (src_code.c), и я не должен его редактировать. Предположим, что я тестирую function(), а в function() вызывается test(). Проблема здесь, test, очень сложная, поэтому я хотел бы сделать ее заглушкой и хочу контролировать только возвращаемое значение и значение выходных аргументов ту заглушку. - person Thảo M. Hoàng; 07.04.2016
comment
Я не могу утверждать, что понимаю все упомянутое там, но мне кажется, что это соответствует тому, что вы ищете. - person MIbrah; 07.04.2016
comment
Спасибо. Ранее я исследовал этот атрибут weak. Но, к сожалению, он только один раз создает ссылку от реальной функции к слабой функции, и как только я вызываю реальную функцию, компилятор всегда перенаправляет ее к слабой функции, и я больше не могу использовать реальную функцию. . Очень грустно! - person Thảo M. Hoàng; 07.04.2016
comment
Знаете ли вы, если test является методом ваших MyClass и function вызовов test, как издеваться над test? - person Thảo M. Hoàng; 09.04.2016