Mocking a function call which happens within a class

987 views
Skip to first unread message

Pradeepa Senanayake

unread,
Feb 10, 2016, 7:14:37 AM2/10/16
to cpputest
Hello All,

I'm a newbie in TDD. I have selected CPPUTest as the test framework for one of our C++ based projects. My team has several members and we are implementing different classes. But the class that I'm developing will eventually create an object of another class which is developed by my other team members.

I need to be able to individually test my class and I believe that test mocking is the way to go.

Assume that I have ClassA.

ClassA
{

SomeFunction()
{

ClassB *class = new ClassB();

class->SomeOtherFunction("abc",3);

}

}

But ClassB is not available yet.

What should be my approach when using mocking for this? This might be a dumb question but this is the first project we are developing using TDD.

Thank you in advance.

Ryan Hartlage

unread,
Feb 10, 2016, 9:59:58 AM2/10/16
to cpputest
Hey Pradeepa,

If you want to test how class A interacts with class B, you probably
want to use Dependency Injection
(https://en.wikipedia.org/wiki/Dependency_injection) to give class A
an instance of class B via class A's constructor (or via one of class
A's methods). This is important for allowing you to provide a fake
implementation of class B without doing link-time mocking.

Once you've done this, you need to create a skeleton (or pure abstract
class) for class B that has the methods that you want class A to have
access to. Once you have this, you can create a subclass of class B
that uses CppUMock to "implement" each of the methods defined for
class B.

Finally, you will provide an instance of the mock subclass of class B
to class A in your tests. This lets you interact with a fake version
of class B in your tests but use a "real" version of class B in your
production code by "injecting" the appropriate version of class B.

Hopefully this helps.

Ryan
> --
> You received this message because you are subscribed to the Google Groups
> "cpputest" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to cpputest+u...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Pradeepa Senanayake

unread,
Feb 11, 2016, 2:25:15 AM2/11/16
to cpputest
Hello Ryan,

Thank you for the descriptive answer. I am currently in the process of altering my the class implementation to make dependency injection possible.

You mentioned "link-time mocking" in you answer? Is this something that I can do with CppUtest. I assume what it might do is intercept function calls without even without dependency injection. I have no idea how it is possible. But is that a possible approach as well.

Also, apart from non-test-ability is there any outright disadvantage of my original class implementation (The one without dependency injection) 

Ryan Hartlage

unread,
Feb 11, 2016, 7:49:33 AM2/11/16
to cpputest
Hey Pradeepa,

Link time mocking means having two versions of a dependency and selecting between them as part of your build. This means that you could have two class B implementations and choose to compile and link one for your production build and choose to compile and link another for your test build. I personally avoid this because it is not very flexible and dependency injection brings other benefits to your code. Especially since you are using C++ and have good language level support for abstract classes I would advise against link time mocking.

For your original class, testability is my primary concern, but flexibility is another. As noted above, dependency injection has many other benefits, for example it allows greater code reuse. Ultimately though, it depends upon your situation and how much you stand to gain from additional testability, reuse, modularity, etc. and there's no "best" way to do things. Sometimes simple but inflexible is all you need :)

Ryan

Pradeepa Senanayake

unread,
Feb 11, 2016, 7:54:04 AM2/11/16
to cpputest
Hello,

I have implemented the mock objects and tested. It is working fine. But I have another question.

First I used,

mock().expectOneCall("Open").onObject(MockInterface).andReturnValue(-1);
CHECK_EQUAL(-1,VCyUsb->Init(NULL,0));
mock().checkExpectations();

It worked perfectly.

Then I used the following mock to verify the operation,

mock().expectOneCall("Open").onObject(MockInterface).andReturnValue(-1);
CHECK_EQUAL(0,VCyUsb->Init(NULL,0));
mock().checkExpectations();

I am returning -1 and expecting 0. It failed as expected., but the error message is following. The mock framework gives an error. It's misleading. 

Error:

error : Failure in TEST(VirtualCyUsbTestGroup, InitFunction_Error_ScannerIntefaceInitFailed)
1>   Mock Failure: Unexpected additional (2th) call to function: Open
1>   EXPECTED calls that did NOT happen:
1>   <none>
1>   ACTUAL calls that did happen (in call order):
1>   (object address: 0298BFF8)::Open -> no parameters
1>  

If I changed the implementation to following, the correct error is reported,

mock().expectOneCall("Open").onObject(MockInterface).andReturnValue(-1);
ret = VCyUsb->Init(NULL,0);
mock().checkExpectations();
CHECK_EQUAL(-1,ret);

Reported error:

error : Failure in TEST(VirtualCyUsbTestGroup, InitFunction_Error_ScannerIntefaceInitFailed)
1>   expected <0>
1>   but was  <-1>
1>   difference starts at position 0 at: <          -1        >
1>                                                 ^

I cannot understand what is happening here. Can anyone explain this.

My mocked function implementation is,

int Open(void)  
{
mock().actualCall("Open").onObject(this);
return mock().returnIntValueOrDefault(0);
}

Thank you.

Ryan Hartlage

unread,
Feb 12, 2016, 11:07:40 AM2/12/16
to cpputest
Hey Pradeepa,

First off, what version of CppUTest are you using?

Second, can you provide a complete, compilable and runnable example
that shows the issue? If this is due to a bug it will help us find it,
and if it is due to a user error we will be able to see that as well.

Thanks,
Ryan

On Thu, Feb 11, 2016 at 7:54 AM, Pradeepa Senanayake

Steven Collins

unread,
Feb 12, 2016, 11:15:53 AM2/12/16
to cppu...@googlegroups.com
I believe your issue is the CHECK_EQUAL() macro, which does double evaluation of its arguments, resulting in a second, unexpected, call to the method. Try LONGS_EQUAL() instead. This is discussed in the documentation of CHECK_EQUAL().

Ryan Hartlage

unread,
Feb 12, 2016, 11:24:13 AM2/12/16
to cppu...@googlegroups.com

Thanks Steven, you're absolutely right.

Ryan

Pradeepa Senanayake

unread,
Feb 18, 2016, 2:31:00 AM2/18/16
to cpputest
Thank you it works.
Reply all
Reply to author
Forward
0 new messages