2
\$\begingroup\$

I have developed Mockingbird a mocking framework for c++, it depends on function injection, here is the code from the repository:

/*
 *  Mockingbird is a simple and a powerful mocking framework for c++.
 *  Copyright (c) Mouaz Chamieh April 2021.
 *  Distributed under the MIT License. Please refer to the LICENSE file at:
 *  https://github.com/muazsh/Mockingbird
 */

#pragma once
#ifndef MOCKINGBIRD
#define MOCKINGBIRD

#define START_MOCK(MockingClass, MockedClass)\
class MockingClass : public MockedClass {\
 public: 

#define INJECTION_SET(FuncName, Substitute)\
private:\
template<class T>\
class FuncName##Class {\
public:\
FuncName##Class():m_func((T)Substitute){}\
T m_func;\
};\
FuncName##Class<decltype(Substitute)> m_##FuncName##Class;\
mutable int m_##FuncName##CallCounter = 0;\
public:\
void Inject##FuncName(decltype(Substitute) sub){\
m_##FuncName##Class.m_func = sub;}\
int Get##FuncName##CallCounter(){return m_##FuncName##CallCounter;}

#define FUNCTION(FuncName,ReturnType, Signature, Substitute, .../*signature variables*/)\
INJECTION_SET(FuncName, Substitute)\
ReturnType FuncName Signature override\
{ m_##FuncName##CallCounter++;\
return m_##FuncName##Class.m_func(__VA_ARGS__);}

#define CONST_FUNCTION(FuncName,ReturnType, Signature, Substitute, .../*signature variables*/)\
INJECTION_SET(FuncName, Substitute)\
ReturnType FuncName Signature const override\
{ m_##FuncName##CallCounter++;\
return m_##FuncName##Class.m_func(__VA_ARGS__);}

#define OVERLOAD_INJECTION_SET(FuncName, Substitute, overloadedMethodNumber)\
private:\
FuncName##Class<decltype(Substitute)> m_##FuncName##Class##overloadedMethodNumber;\
mutable int m_##FuncName##overloadedMethodNumber##CallCounter = 0;\
public:\
void Inject##FuncName(decltype(Substitute) sub){\
m_##FuncName##Class##overloadedMethodNumber.m_func = sub;}\
int Get##FuncName##overloadedMethodNumber##CallCounter(){return m_##FuncName##CallCounter;}

#define FUNCTION_OVERLOADING(FuncName,ReturnType, Signature, Substitute, overloadedMethodNumber, .../*signature variables*/)\
OVERLOAD_INJECTION_SET(FuncName, Substitute, overloadedMethodNumber)\
ReturnType FuncName Signature override\
{ m_##FuncName##overloadedMethodNumber##CallCounter++;\
return m_##FuncName##Class##overloadedMethodNumber.m_func(__VA_ARGS__);}

#define CONST_FUNCTION_OVERLOADING(FuncName,ReturnType, Signature, Substitute, overloadedMethodNumber, .../*signature variables*/)\
OVERLOAD_INJECTION_SET(FuncName, Substitute, overloadedMethodNumber)\
ReturnType FuncName Signature const override\
{ m_##FuncName##overloadedMethodNumber##CallCounter++;\
return m_##FuncName##Class##overloadedMethodNumber.m_func(__VA_ARGS__);}

#define END_MOCK(MockingClass)\
};

#endif // !MOCKINGBIRD

and here is an example on how it works:

Assume you have the following class Foo and you need to mock:

struct MyStruct{
    int x, y;
};

class Foo{
public:
    virtual const MyStruct CreateMyStruct(int x, int y) { return MyStruct{ x,y }; }
}; 

Then you write a testing fixture only once for the whole project:

const MyStruct CreateMyStructDummy(int x, int y) { return MyStruct{0,0}; }

START_MOCK(FooMock, Foo)
FUNCTION(CreateMyStruct, const MyStruct, (int x, int y), &CreateMyStructDummy, x, y)
END_MOCK(FooMock)

Then in the tests you write the desired substitute for example:

const MyStruct CreateMyStructSubstitute(int x, int y) { return MyStruct{ x + 10, y + 10 }; }

and inject it like:

FooMock fooMock;
fooMock.InjectCreateMyStruct(CreateMyStructSubstitute); // Mocking function injection.
auto created = fooMock.CreateMyStruct(5,5);
EXPECT_EQ(created.x, 15);
EXPECT_EQ(created.y, 15);

The code is in the file Mockingbird.hpp and totally depends on macros, it is short and straightforward, I ask for reviewing the code on github here, thanks.

\$\endgroup\$
7
  • \$\begingroup\$ @pacmaninbw I see, in this case what about the license once I post my code in here, I have read some questions like this but it is too old and not sure if this is still valid or even it can be dragged to codereview community. \$\endgroup\$ – muaz Apr 19 at 20:09
  • 2
    \$\begingroup\$ @muaz IANAL TINLA. By posting you submit the code under CC BY-SA 4.0 and MIT (if you include the license and stuff) revewers have the option to provide answers only in CC BY-SA 4.0. If a reviewer submits an only CC BY-SA answer you can ask the answerer nicely to allow you to use the code as MIT. \$\endgroup\$ – Peilonrayz Apr 19 at 21:04
  • 4
    \$\begingroup\$ Posting to stackexchange puts the code available on SE under the CC-BY-SA-4.0 license, so long as you have the rights to perform such a licensing. Users of the code could then choose which license they'd use. For MIT that would require carrying over the MIT license declaration, for CC-BY that requires licensing derivative works under CC-BY and attributing to the original author. \$\endgroup\$ – Vogel612 Apr 19 at 21:04
  • 1
    \$\begingroup\$ Welcome to Code Review! Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see What should I do when someone answers my question? as well as what you may and may not do after receiving answers. \$\endgroup\$ – Sᴀᴍ Onᴇᴌᴀ Apr 20 at 21:07
  • 1
    \$\begingroup\$ Another reason for not updating the code after an answer has been posted is that everyone should see what the person writing the review saw so they understand the review. \$\endgroup\$ – pacmaninbw Apr 20 at 21:22
3
\$\begingroup\$

You make the code really hard to read:

#define INJECTION_SET(FuncName, Substitute)\
private:\
template<class T>\
class FuncName##Class {\
public:\
FuncName##Class():m_func((T)Substitute){}\
T m_func;\
};\
FuncName##Class<decltype(Substitute)> m_##FuncName##Class;\
mutable int m_##FuncName##CallCounter = 0;\
public:\
void Inject##FuncName(decltype(Substitute) sub){\
m_##FuncName##Class.m_func = sub;}\
int Get##FuncName##CallCounter(){return m_##FuncName##CallCounter;}

just because it is a macro does not mean it needs to be unreadable. You can still format it nicely for the maintainer.

#define INJECTION_SET(FuncName, Substitute)                         \
                                                                    \
    private:                                                        \
    template<class T>                                               \
    class FuncName##Class                                           \
    {                                                               \
        public:                                                     \
            FuncName##Class():m_func((T)Substitute){}               \
            T m_func;                                               \
    };                                                              \
                                                                    \
    FuncName##Class<decltype(Substitute)> m_##FuncName##Class;      \
                                                                    \
    mutable int m_##FuncName##CallCounter = 0;                      \
    public:                                                         \
    void Inject##FuncName(decltype(Substitute) sub)                 \
    {                                                               \
        m_##FuncName##Class.m_func = sub;                           \
    }                                                               \
    int Get##FuncName##CallCounter()                                \
    {                                                               \
        return m_##FuncName##CallCounter;                           \
    }
\$\endgroup\$
2
  • \$\begingroup\$ I think the unreadability goes a long way towards explaining why it sat for two days waiting for a reviewer. It certainly discouraged me. \$\endgroup\$ – Toby Speight Apr 20 at 20:07
  • \$\begingroup\$ Okay thanks I edited and reformatted code, I hope it is more readable now. \$\endgroup\$ – muaz Apr 20 at 21:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.