Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IgnoreParameter not working? #12

Open
madjwilliams opened this issue May 2, 2021 · 4 comments
Open

IgnoreParameter not working? #12

madjwilliams opened this issue May 2, 2021 · 4 comments

Comments

@madjwilliams
Copy link

First off, thank you for this wonderful tool. It is excellent and will save us a lot of time creating and maintaining mocks!

Sometimes the TESTs we write have no control over the mock's object instance. Only the unit under test does. So there is a need to ignore the class's object instance check. However, I am seeing a problem attempting to skip the object check. For example:

`
// class6.h
namespace namespace1
{
class class6
{
public:
void method1(const char* p1, int p2);
};
}

// class6.cpp
/*
#include
#include "class6.h"
namespace namespace1
{
void class6::method1(const char* p1, int p2)
{
if ((strcmp(p1, "hello") == 0) && (p2 == 42))
{
// do something
}
}
}*/

// class6_mock.cpp
/*

  • This file has been auto-generated by CppUTestMock v0.4.
  • Contents will NOT be preserved if it is regenerated!!!
  • Generation options: -x
    /
    //#include "class6.h"
    #include <CppUTestExt/MockSupport.h>
    void namespace1::class6::method1(const char
    p1, int p2)
    {
    mock().actualCall("namespace1::class6::method1").onObject(this).withStringParameter("p1", p1).withIntParameter("p2", p2);
    }

// class6_expect.hpp
/*

  • This file has been auto-generated by CppUTestMock v0.4.
  • Contents will NOT be preserved if it is regenerated!!!
  • Generation options: -x
    /
    #include <CppUMockGen.hpp>
    //#include "class6.h"
    #include <CppUTestExt/MockSupport.h>
    namespace expect {
    namespace namespace1$ {
    namespace class6$ {
    MockExpectedCall& method1(CppUMockGen::Parameter<const namespace1::class6
    > object, CppUMockGen::Parameter<const char*> p1, CppUMockGen::Parameter p2, bool return);
    MockExpectedCall& method1(unsigned int numCalls, CppUMockGen::Parameter<const namespace1::class6*> object, CppUMockGen::Parameter<const char*> p1, CppUMockGen::Parameter p2, bool return);
    }
    }
    }

// class6_expect.cpp
/*

  • This file has been auto-generated by CppUTestMock v0.4.
  • Contents will NOT be preserved if it is regenerated!!!
  • Generation options: -x
    /
    //#include "class6_expect.hpp"
    namespace expect {
    namespace namespace1$ {
    namespace class6$ {
    MockExpectedCall& method1(CppUMockGen::Parameter<const namespace1::class6
    > object, CppUMockGen::Parameter<const char*> p1, CppUMockGen::Parameter p2)
    {
    bool ignoreOtherParams = false;
    MockExpectedCall& expectedCall = mock().expectOneCall("namespace1::class6::method1");
    if (!object.isIgnored()) { expectedCall.onObject(const_castnamespace1::class6*(object.getValue())); }
    if (p1.isIgnored()) { ignoreOtherParams = true; }
    else { expectedCall.withStringParameter("p1", p1.getValue()); }
    if (p2.isIgnored()) { ignoreOtherParams = true; }
    else { expectedCall.withIntParameter("p2", p2.getValue()); }
    if (ignoreOtherParams) { expectedCall.ignoreOtherParameters(); }
    return expectedCall;
    }
    MockExpectedCall& method1(unsigned int numCalls, CppUMockGen::Parameter<const namespace1::class6*> object, CppUMockGen::Parameter<const char*> p1, CppUMockGen::Parameter p2)
    {
    bool ignoreOtherParams = false;
    MockExpectedCall& expectedCall = mock().expectNCalls(numCalls, "namespace1::class6::method1");
    if (!object.isIgnored()) { expectedCall.onObject(const_castnamespace1::class6*(object.getValue())); }
    if (p1.isIgnored()) { ignoreOtherParams = true; }
    else { expectedCall.withStringParameter("p1", p1.getValue()); }
    if (p2.isIgnored()) { ignoreOtherParams = true; }
    else { expectedCall.withIntParameter("p2", p2.getValue()); }
    if (ignoreOtherParams) { expectedCall.ignoreOtherParameters(); }
    return expectedCall;
    }
    }
    }
    }

void unit_under_test(const char* p1, int p2)
{
namespace1::class6 class6_object_instance;
class6_object_instance.method1(p1, p2);
}

TEST_GROUP(class6)
{
void setup() {}
void teardown() { mock().clear(); }
};

TEST(class6, method1_check_object)
{
namespace1::class6 class6_mock;

expect::namespace1$::class6$::method1(&class6_mock, "hello", 42);
class6_mock.method1("hello", 42);
mock().checkExpectations();

}

TEST(class6, method1_ignore_object)
{
// The unit under test controls the lifecycle of the mock object so ignore the object parameter!
expect::namespace1$::class6$::method1(CppUMockGen::IgnoreParameter::YES, "hello", 42);
unit_under_test("hello", 42); // <-- EXCEPTION here!
mock().checkExpectations();
}

/*
Failure in TEST(class6, method1_ignore_object)
MockFailure: Function called on an unexpected object: namespace1::class6::method1
Actual object for call has address: <007FE96B>
EXPECTED calls that DID NOT happen related to function: namespace1::class6::method1
namespace1::class6::method1 -> const char* p1: , int p2: <42>
ACTUAL calls that DID happen related to function: namespace1::class6::method1

..
Errors (1 failures, 2 tests, 2 ran, 2 checks, 0 ignored, 0 filtered out, 24 ms)
*/
`

I used your example in the documentation page, "namespace1::class6::method", to generate the class6 mock.

@madjwilliams
Copy link
Author

Hmmm. The code did not show up formatted nicely in my original post above. So let me attach the sample source code that shows the error.

example_unit_test.cpp.txt

@madjwilliams
Copy link
Author

Jesús González,
I was playing around this some more and I do have a work around. I am not sure this is the best way to handle it nor am I sure that the original problem is just me? I did modify the CppUMockGen mock and expect code to the following:

void namespace1::class6::method1(const char* p1, int p2)
{
const bool __object__isIgnored = mock().getData("namespace1::class6::method1__object___isIgnored").getBoolValue();
namespace1::class6* thisPtr = (__object__isIgnored) ? nullptr : this;
mock().actualCall("namespace1::class6::method1").onObject(thisPtr).withStringParameter("p1", p1).withIntParameter("p2", p2);
}

And the expect:

namespace expect {
namespace namespace1$ {
namespace class6$ {
MockExpectedCall& method1(CppUMockGen::Parameter<const namespace1::class6*> object, CppUMockGen::Parameter<const char*> p1, CppUMockGen::Parameter p2)
{
bool ignoreOtherParams = false;
MockExpectedCall& expectedCall = mock().expectOneCall("namespace1::class6::method1");
mock().setData("namespace1::class6::method1__object___isIgnored", object.isIgnored());
if (!object.isIgnored()) { expectedCall.onObject(const_castnamespace1::class6*(object.getValue())); }
if (p1.isIgnored()) { ignoreOtherParams = true; }
else { expectedCall.withStringParameter("p1", p1.getValue()); }
if (p2.isIgnored()) { ignoreOtherParams = true; }
else { expectedCall.withIntParameter("p2", p2.getValue()); }
if (ignoreOtherParams) { expectedCall.ignoreOtherParameters(); }
return expectedCall;
}
}
}
}

@madjwilliams
Copy link
Author

Now that I figured out the code blocks. Let's try this modification code again.

// new mock modification: skip object check by passing in "nullptr"
void namespace1::class6::method1(const char* p1, int p2)
{
    const bool __object__isIgnored = mock().getData("namespace1::class6::method1__object___isIgnored").getBoolValue();
    namespace1::class6* thisPtr = (__object__isIgnored) ? nullptr : this;
    mock().actualCall("namespace1::class6::method1").onObject(thisPtr).withStringParameter("p1", p1).withIntParameter("p2", p2);
}

And the expect changes:

// new expect modification: pass the object.isIgnored() flag
namespace expect {
    namespace namespace1$ {
        namespace class6$ {
            MockExpectedCall& method1(CppUMockGen::Parameter<const namespace1::class6*> __object__, CppUMockGen::Parameter<const char*> p1, CppUMockGen::Parameter<int> p2)
            {
                bool __ignoreOtherParams__ = false;
                MockExpectedCall& __expectedCall__ = mock().expectOneCall("namespace1::class6::method1");
                mock().setData("namespace1::class6::method1__object___isIgnored", __object__.isIgnored());
                if (!__object__.isIgnored()) { __expectedCall__.onObject(const_cast<namespace1::class6*>(__object__.getValue())); }
                if (p1.isIgnored()) { __ignoreOtherParams__ = true; }
                else { __expectedCall__.withStringParameter("p1", p1.getValue()); }
                if (p2.isIgnored()) { __ignoreOtherParams__ = true; }
                else { __expectedCall__.withIntParameter("p2", p2.getValue()); }
                if (__ignoreOtherParams__) { __expectedCall__.ignoreOtherParameters(); }
                return __expectedCall__;
            }
        }
    }
}

@madjwilliams
Copy link
Author

I am not sure the previous modification will work? So let's try another example.
Let's start with a much simpler class to mock:

#ifndef CLASS2MOCK_H
#define CLASS2MOCK_H
class Class2Mock
{
public:
    void method();
};
#endif//CLASS2MOCK_H

Run it through this AWESOME CppUMockGen tool and get the mock:

// Slight modification to handle ignoring the object by using mock().setData()
void Class2Mock::method()
{
#if 1 // enhancement
    static std::size_t actualCallCount = 0;
    const std::string callString = "Class2Mock::method::call(" + std::to_string(++actualCallCount) + ").isIgnored";
    const bool __object__isIgnored = mock().getData(callString.c_str()).getBoolValue();
    Class2Mock* thisPtr = (__object__isIgnored) ? nullptr : this;
    mock().actualCall("Class2Mock::method").onObject(thisPtr);
#else // original mock code
    mock().actualCall("Class2Mock::method").onObject(this);
#endif
}

Now the expect generated code:

namespace expect { namespace Class2Mock$ {
MockExpectedCall& method(CppUMockGen::Parameter<const Class2Mock*> __object__)
{
#if 1 // enhancement
    return method(1, __object__);
#endif
}
MockExpectedCall& method(unsigned int __numCalls__, CppUMockGen::Parameter<const Class2Mock*> __object__)
{
#if 1 // enhancement
    static std::size_t expectedCallCount = 0;
    for (unsigned int i = 0; i < __numCalls__; ++i)
    {
        const std::string callString = "Class2Mock::method::call(" + std::to_string(++expectedCallCount) + ").isIgnored";
        mock().setData(callString.c_str(), __object__.isIgnored());
    }
    MockExpectedCall& __expectedCall__ = mock().expectNCalls(__numCalls__, "Class2Mock::method");
    Class2Mock* thisPtr = (__object__.isIgnored()) ? nullptr : const_cast<Class2Mock*>(__object__.getValue());
    __expectedCall__.onObject(thisPtr);
    return __expectedCall__;
#else // original expect code
    MockExpectedCall& __expectedCall__ = mock().expectNCalls(__numCalls__, "Class2Mock::method");
    if(!__object__.isIgnored()) { __expectedCall__.onObject(const_cast<Class2Mock*>(__object__.getValue())); }
    return __expectedCall__;
#endif
}

Again, I am not sure this is the best way to fix the original issue? But this does work for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant