Try Google Mock with C

Overview

It's a special Obon holiday, but I don't feel like having a summer cold, so I poke Google Mock in my room. I'm using C for some reason at work, so I've summarized how to use Google Mock for C code. This time is the introduction.

Try this time

Environment I tried

Contents

Introduce Google Test

It seems that there are many articles of this degree, so I will go straight to it. I think the following will also be helpful. https://qiita.com/shohirose/items/30e39949d8bf990b0462

I will write a test code for the time being

Suppose you have code like this. (I omit Include, but I'm doing it well)

sub.c


#include "mystr.h"

int test1(const char* str)
{                                             
    fprintf(stderr, "test print %s\n", str);  
    return str_analyze(str, 10);              
}                                             

mystr.c


int str_analyze(const char* str, int max)          
{                                            
    int l;                                   
    for (l = 0; l < max; l++) {              
        if (str[l] == '\0') break;           
    }                                        
    return l;                                
}                                            

The code to test the return value of this test1 () function would be:

sub_test.cpp


TEST(sub_test, test01)
{
    EXPECT_EQ(6, test1(": test"));
}

For the time being, these placements and builds are done as shown in the site below. https://techblog.kayac.com/google-test

g++ -std=c++11 test/sub_test.cpp -I. -Isrc -Igoogletest/include -Lgoogletest -lgtest -lgtest_main -lpthread

If you do this, the ʻa.out` file will be generated as a test program and you can run the test.

Prepare the test environment by changing the arrangement

If this is left as it is, the source code and development code of Google Test will be fused, so try changing the arrangement as follows.

|
+- GoogleTest
|  + ...
|
+- work 
   + sub.c
   + sub.h
   + mystr.c
   + mystr.h

The build at this time can be done by slightly changing the command written above, but it is annoying. So I will use scons that I learned at work recently. I wrote it while looking at the following. [^ 2] https://scons.org/doc/0.97/HTML/scons-user/x628.html

SConstruct


env = Environment()

env.Program(
    target='sub_test',
    #You can debug with gdb if you don't have to add debug options
    CCFLAGS = '-g',
    source = [
        "sub.c",
        "mystr.c",
        "sub_test.cpp",
    ],
    LIBPATH = [
        "../googletest/lib",
        "../googletest/googlemock/lib",
    ],
    LIBS = [
        'libgtest.a',
        'libgtest_main.a',
        'libgmock.a',
        'libgmock_main.a',
        'pthread',
    ],
    CPPPATH = [
        '.',
        '../googletest',
        '../googletest/googletest',
        '../googletest/googletest/include',
        '../googletest/googlemock',
        '../googletest/googlemock/include',
    ],
)

Well, this is not so good because the intermediate products are mixed in the source code, but this time it became annoying, so it is as it is.

Use Google Mock

The contents from here refer to the following page in addition to the official document.

  1. https://futurismo.biz/archives/306
  2. https://stackoverflow.com/questions/31989040/can-gmock-be-used-for-stubbing-c-functions

Why use mock

The reason for using Mock is also explained in the translation of the official documentation.

http://opencv.jp/googlemockdocs/fordummies.html

In a normal program, you will use a real implementation of this interface. The test uses a mock implementation instead. This makes it easy to check which drawing primitives were called from the program, what the arguments are, and what the order is. Tests written this way are very robust (they don't break due to changes in machine antialiasing), are easy to decipher and maintain (the purpose of the test is expressed in code rather than in an image file). ), And the test execution is much faster.

Personally, I think that white-box testing checks such as whether they are called and the order are important for using Mock.

Now, let's create a Mock of str_analyze () as a module to Mock mystr. However, since Google Mock is for C ++, it cannot be used as it is for C.

Create interface class and Mock class

It doesn't seem to be written on the official page, but it seems that you need to create an interface class [^ 3] to use Google Mock. So, mystr is C, but if it is C ++ ... I will create a header file.

mock/mock_mystr.h


class Mystr {
public:
Mystr(void){}
virtual ~Mystr(void){}
virtual int str_analyze(const char* str, int max) = 0;
};

Continue writing the corresponding Mock class.

mock/mock_mystr.h


class MockMystr : public Mystr {
 public:
  MockMystr(){}
  ~MockMystr(){}
  MOCK_METHOD2(str_analyze,
      int(const char* str, int max));
};

Corresponds to calls from the code under test

Of course, you can't call a C ++ class from C code, and it's not good to rewrite the code you're testing. Therefore, create a Mock as shown in Reference Page 2 above.

mock/mock_mystr.h


extern MockMystr* mystr_mock;

extern "C" int str_analyze(const char* str, int max)
{
    int ret = mystr_mock->str_analyze(str, max);
    return ret;
}

I think that ʻextern "C" `is generally necessary for suppressing mangling.

Switch builds to use Mock

So far, I've almost created a Mock for the str_analyze () function that I wanted to make into a Mock, so I'll use this. First, make the body of str_analyze () invisible.

SConstruct


    source = [
        "sub.c",
-        "mystr.c",
+       #"mystr.c",
        "sub_test.cpp",
    ],

Then add Mock references and settings to your test program.

sub_test.cpp


#include "mock/mock_mystr.h"

MockMystr* mystr_mock;

//It seems that such a Main function is necessary when using Mock
int main(int argc, char** argv)
{
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}

TEST(sub_test, test01)
{
    mystr_mock = new MockMystr();

    const char* test_str = ": test";
    int test_str_len = strlen(test_str);
    EXPECT_CALL(mystr_mock, str_analyze(_, _))
        .WillOnce(::testing::Return(test_str_len));

I've also written an Expectation for calling Mock above. ʻEXPECT_CALL` is the one. Now you can test.

Summary

After introducing Google Test and arranging the layout a little, I made it possible to use Google Mock in C as well. There are several ways to use Google Mock in C, but this time I'm using C's Google Mock in the next step.

Creating an interface class and creating a Mock function is a hassle, but since each is a simple description, I think it could be automated.

Bonus: interface class and vtable for error

For this time, when I was playing with Mock, I got the following build error and suffered for a while.

sub_test.o: In function `Mystr::Mystr()':
/var/work/c/test/work/./mystr.h:5: undefined reference to `vtable for Mystr'
sub_test.o: In function `Mystr::~Mystr()':
/var/work/c/test/work/./mystr.h:6: undefined reference to `vtable for Mystr'

Ah, yeah, I felt like vtable is the one I see from time to time. In my life so far, I've been playing around with it and this error has disappeared! I've spent time in Japan, but this time I was motivated to investigate. What is virtual? Or vtable? With that feeling. Then I finally arrived at the following page.

http://yukichanko.hatenablog.com/entry/20110113/1294932335

Well, this page is not the answer either, but in short, if you create a half-finished interface class as shown below, you will get an error like this.

class Mystr {
public:
Mystr(void){};
virtual ~Mystr(void){};
virtual int str_analyze(const char* str, int max); //Not a pure virtual function
};

I don't think I can conclude that the interface class is out of order by reading the g ++ error message as it is, but does this make sense?

Test code I made

I will publish it below. work01 has no mock and work02 has mock.

https://github.com/hakua-doublemoon/GoogleTestStudy

[^ 1]: I used Windows Docker for the first time this time, but I felt that I didn't need WSL if I had this. Performance may be different, but what you can do is not much different. Then I thought that Docker would be easier to manage. [^ 2]: Somehow SConstruct is the same as Make, and I can't find a cool way to write it ... [^ 3]: It wasn't until I came here that I somehow understood the interface class. I was a little convinced that it felt like a Rust trait. [^ 4]: It may be better to use a smart pointer as shown in the reference page, but this time I haven't used it.

Recommended Posts

Try Google Mock with C
Try OpenCV with Google Colaboratory
Try using Python with Google Cloud Functions
Try running Google Chrome with Python and Selenium
Authenticate Google with Django
Try scraping with Python.
Debugging C / C ++ with gdb
Tested with boto3 + mock
Container-like # 2 made with C
Try SNN with BindsNET
Try NNabla's C ++ API
Try regression with TensorFlow
Try embedding Python in a C ++ program with pybind11
Try function optimization with Optuna
Try deep learning with TensorFlow
Try using PythonTex with Texpad.
Test embedded software with Google Test
ABC163 C problem with python3
Try edge detection with OpenCV
Try implementing RBM with chainer.
Try StyleGAN on Google Colaboratory
Try using matplotlib with PyCharm
Try programming with a shell!
Try GUI programming with Hy
Try an autoencoder with Pytorch
Try Python output with Haxe 3.2
Try matrix operation with NumPy
Study Python with Google Colaboratory
Try implementing XOR with PyTorch
Try running CNN with ChainerRL
About learning with google colab
Try various things with PhantomJS
Try Deep Learning with FPGA
Try running Python with Try Jupyter
Format C source with pycparser
Try implementing perfume with Go
Mount google drive with google-drive-ocamlfuse
Access Google Drive with Python
Try Selenium Grid with Docker
Try face recognition with Python
Translate PHP_UML --help with google translate
ABC188 C problem with python3
Try machine learning with Kaggle
Try TensorFlow MNIST with RNN
Try building JupyterHub with Docker
Try using folium with anaconda
ABC187 C problem with python
Try to implement linear regression using Pytorch with Google Colaboratory
Try to display google map and geospatial information authority map with python
Try Deep Learning with FPGA-Select Cucumbers
Google Test / Mock personal cheat sheet
Try scraping with Python + Beautiful Soup
Solve ABC163 A ~ C with Python
Reinforcement learning 13 Try Mountain_car with ChainerRL.
Create Awaitable with Python / C API
Try to operate Facebook with Python
Writing C language with Sympy (metaprogramming)
Try singular value decomposition with Python
Try deep learning with TensorFlow Part 2
Try http-prompt with interactive http access
Try audio signal processing with librosa-Beginner