How to use Google Mock to mock external GUI API in order to uni test own code.

1,094 views
Skip to first unread message

vlad

unread,
Jun 22, 2009, 6:11:10 PM6/22/09
to Google C++ Mocking Framework
Hello,

I'm creating a 3D videogame. I'm using an external 3D engine for this.
I'm writing Unit Tests for the code that I'm creating, but in order to
do this I must mock the interface to the 3D engine; the reason being
that if I don't, the engine will initialize a window, start OpenGL
calls, expect mouse clicks and keystrokes, and most importantly: enter
a main loop from where it won't come back until closing the window. I
need to mock the engine objects to make sure my Unit Tests can run in
a command line enviroment. The mock objects just returns expected
values: true, false, pointers to other mocked objects, etc. I'm you
guys get the point.

Now, the issue is that I don't really want to test the classes for the
3D engine, I just need them mocked so I can proceed with testing my
own code. However, it's important to set up those calls to the mocked
3D API to return different results so that I can tests different
outcomes on my code.

Another issue is that the calls to the 3D API are deep within my code.
So a Unit Test would look something like:

TEST_F(Interpreter, InterpreterCreatedManually)
{
Interpreter* i;
EXPECT_NO_THROW(i = new Interpreter());
EXPECT_NO_THROW(delete i);
}

In the above example, the 3D engine code is deep within the
Interpreter code, so I don't know how I would setup the mockup
objects, to return different values, for instance. Also, the
Interpreter, and/or some of it's subclasses make use of Global
pointers to the 3D engine (The engine's API is designed this way).
Those global objects are also mocked, of course.

Maybe the essential question is: Can I auto-setup the mock objects so
that when my code creates them, the mock objects can set themselves up
and my code can remain unaware of them?

Please, comments are welcomed about anything I mentioned above.


Thanks,
Vlad

Jared Wein

unread,
Jun 22, 2009, 10:52:16 PM6/22/09
to vlad, Google C++ Mocking Framework
Hi Vlad,

I'm not sure how your code is structured, but this is what I interpreted it as. You have a class that accesses a global object like so:

extern Renderer theRenderer;

void Interpreter::ShowNewGameLevel()
{
  theRenderer.DrawFloor();
  /* do something interesting */
}

If you are testing against a static library, you could build the static library and then remove the definition for the Renderer object from the static library and create a mock Renderer within the test project. 

MockRenderer theRenderer;

TEST_F(Interpreter, InterpreterCreatedManually)
{
 Interpreter* i;
 EXPECT_NO_THROW(i = new Interpreter());
 EXPECT_NO_THROW(delete i);
}

- jared

vlad

unread,
Jun 22, 2009, 11:17:44 PM6/22/09
to Google C++ Mocking Framework

This is what I've come up with so far, though I'm still short
because I don't know how to implement GraphicFoo::mockCopy().

In the mock object, we define a couple of extra operations that don't
belong to the original's object interface; these functions help with
changing the mock object instances expectations on a per instance
basis
so that different calls accessing and creating mock objects can test
different behaviors.

class GraphicFoo
{
enum Private {
Private = 1
};

// Make a private constructor that only the mock* functions
// can access to make sure the first Instance get's
initialized
// to the default expectations.
GraphicFoo(Private) {mockDefault(this);}
public:
static GraphicFoo* mockTemplate()
{
if (!mMockTemplate) {
mMockTemplate = new GraphicFoo(Private);
}

return mMockTemplate;
}

static void mockDefault(GraphicFoo* me)
{
// We should somehow reset all expectation
// to their default behavior (per Google Mock)
// and then set the default expectations for this obj.
EXPECT_CALL(*me, Bar(_))
.Times(AnyNumber());
}

static void mockCopy(GraphicFoo* me)
{
GraphicFoo* tpl = mockTemplate();
/*
How to copy the expectations from tpl to me, without
copying the state information, such as how many times Bar was already
called?
*/
}

// All the constructors would copy the current
// expectation template to new instances.
GraphicFoo() {mockCopy(this);}
MOCK_METHOD(Bar, int(const Canvas* c));
}

/***************************************************************/

TEST(Graphics, Test)
{
// Assume GraphicDrawer::Go() calls GraphicFoo::Bar() n times,
// depending on the arg supplied to Go(), and it returns the
// actual amount of times that Bar() ran.
// With the default behavior of GraphicFoo, per our setup of
// the mock object, this would work fine.
GraphicDrawer* d = nullptr;
EXPECT_NO_THROW(d = new GraphicDrawer);
EXPECT_EQ(300, d->Go(300));
EXPECT_NO_THROW(delete d);

// Now, let's assume we want to make sure GraphicFoo::Bar() only
// runs once. I would like to be able do something like this:
GraphicFoo* f = GraphicFoo::mockTemplate();
EXPECT_CALL(*f, Bar(_))
.Times(1);

// The above would change the default behavior of how instances
// of the mock object GraphicFoo normally responde. From now on,
// all instance of GraphicFoo will have the new expectations
// because those expectations are copied to new instances from
// the template within the mock object.
// This would be useful to change the behavior between call.
// If we want to reset the behavior, we do this:
// GraphicFoo::mockDefault(g)
}


Again, the driving requirement for this is that I want to test my
code, which makes use of some graphic library. I don't really care
about the graphic library code, but I need to test the different
values they return. Also, more importantly, I want to make sure the
graphics library doesn't go into an infinite loop when it starts the
window to draw.

Vlad Losev

unread,
Jun 23, 2009, 1:50:36 AM6/23/09
to vlad, Google C++ Mocking Framework

The common pattern used for this kind of testing is Dependency Injection. The essence of it is that you do not create or obtain singleton instances of objects you use inside your code but create those instances outside of your objects and pass them into your object constructors. I consider this blog post by James Shore the best introduction into the topic. All other articles on it that I have seen try to make it look more complex then it really is. Google Mock adds value to this concept by allowing easy generation and control of mock objects.

vlad

unread,
Jun 23, 2009, 5:45:35 AM6/23/09
to Google C++ Mocking Framework
Thank you for the link, similarly named fellow human being. Also you,
Jared Wein.

I hope this turn out to be of help, but I don't think that's what I
need.

Consider this common use case I have:

I have 3 classes, A, B, and C which make use of external engine's
GraphicNode class.
Class C makes use of it directly, while class A and B indirectly, by
way of C.

Lets further assume the calls go something like:
a.foo() -> c.bar() -> node.attach(a)
b.bez() -> c.bar() -> node.attach(b)

I want to test the A and B classes:

TEST(Graphics, Test)
{
...
// Here, GraphicNode needs to be set up a certain way.
ASSERT_TRUE(a.foo());

// Here, GraphicNode needs to be set up a different way.
ASSERT_TRUE(b.foo());

// We never use class C directly, which is the one talking to
GraphicNode.
...
}


AT this point, GraphicNode is a mocked up object. The problem is
setting up it's
expectations, because the node mock is unrecheable to the TEST().

JJ

unread,
Jun 23, 2009, 6:43:39 AM6/23/09
to Google C++ Mocking Framework


On Jun 23, 9:45 am, vlad <megav...@gmail.com> wrote:
> Thank you for the link, similarly named fellow human being. Also you,
> Jared Wein.
>
> I hope this turn out to be of help, but I don't think that's what I
> need.
>
> Consider this common use case I have:
>
> I have 3 classes, A, B, and C which make use of external engine's
> GraphicNode class.
> Class C makes use of it directly, while class A and B indirectly, by
> way of C.

In the case, a sensible thing to do would be to test class C by
mocking GraphicNode,
but test A and B by mocking C. If A and B are using C, not
GraphicNode, then they need
to know about a mock GraphicNode even less than a real one.
Generally, mock object testing is much easier if you mock only a
class' immediate neighbours.

>
> Lets further assume the calls go something like:
> a.foo() -> c.bar() -> node.attach(a)
> b.bez() -> c.bar() -> node.attach(b)
>
> I want to test the A and B classes:
>
> TEST(Graphics, Test)
> {
> ...
> // Here, GraphicNode needs to be set up a certain way.
> ASSERT_TRUE(a.foo());
>
> // Here, GraphicNode needs to be set up a different way.
> ASSERT_TRUE(b.foo());
>
> // We never use class C directly, which is the one talking to
> GraphicNode.
> ...

So this is really three tests, not two: one test for class C, testing
that c.bar() calls
node.attach() in the way that you want, and one test each for A and B
testing that foo()
calls c.bar() in the way that you want. The latter tests don't need to
test that node is
handled correctly, because you've already tested that in the first
test.

Vlad Losev

unread,
Jun 23, 2009, 12:32:26 PM6/23/09
to vlad, Google C++ Mocking Framework
Hi Vlad,


Here is an example of using DI for 3 class interaction. Please note that you don't have to set expectations on the mock node anywhere but in the test itself.

class C {
 public:
  C(GraphicNode* node) : node_(node) {}
  void set_a(A* a) { a_ = a; }  // or whatever way C gets a hold of an A
  void bar() { node_->attach(a_); }
 private:
  GraphicNode* node_;
  A* a_;
};

class A {
 public A(C* c) : c_(c) {}
 void foo() { c_->bar(); }
 private:
  C* c_;
};

TEST(TestA, CalingFooAttachesAToTheNode) {
  MockNode node;
  C* c = new C(&node);
  A a(c);
  EXPECT_CALL(node, attach(a));  // attach will be called once with argument a.
  ASSERT_TRUE(a.foo());
}

I should also note that Jared is right: testing classes in isolation is preferable. Such tests are simpler to write and simpler for others to make sense of.

Regards,
Vlad

vlad

unread,
Jun 23, 2009, 1:43:20 PM6/23/09
to Google C++ Mocking Framework
Vlad,

This sort of test is pretty straight forward. I'm more worried about
when I can't pass objects into functions because the class I'd be
testing would call other classes, which call others, which call
others, which call the mock object I want to setup.

I thinks this summarizes it well:
Reply all
Reply to author
Forward
0 new messages