Mocking Ogre

A place for users of OGRE to discuss ideas and experiences of utilitising OGRE in their games / demos / applications.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Mocking Ogre

Post by jacmoe »

It's not what you think it is. :wink:

But has anyone thought about - or even tried - using a mocking library with Ogre?

I know that there's a null renderer *somewhere*, but a mock might be cleaner - and easier to do.

We have Google Mocking Library

For those of you who don't know what a mocking library is, read this:
http://code.google.com/p/googlemock/wik ... ck_Turtles

And stop sniggering! :)

Mocking is good when you use Test Driven Development (TDD).
You mock the graphics API, third-party libraries, etc. In order to do tests more effectively, you narrow the scope of your test(s).

Anyone?
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Re: Mocking Ogre

Post by jacmoe »

If Google Mocking Library is too heavy for you - it is for me - you can try MockItNow with UnitTest++ :wink:

Would be cool if we could mock the rendersystem plugins. :)
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
wally
Halfling
Posts: 89
Joined: Tue Apr 01, 2008 1:10 am
Location: Canberra, Australia

Re: Mocking Ogre

Post by wally »

Slightly old post but...
Did you proceed using MockItNow, and if so how did it go?
From a quick read over that site, it looks like a nice light solution without having to define any wrapper classes. Though the distributed version is only useful for msvc compilers (fine for me initially but it would be nice to be able to run tests on mac as well).

One thing that isn't clear from my quick read: does it avoid having to create real instances of Ogre objects? (Entity etc can be problematic to construct without some Ogre initialisation).

I'm already started using Google Mock (and Google Test) with Ogre.
It's early days, but the basic idea is
1) Define an adapter interface for any Ogre class you want to be able to mock. Interface is identical to the Ogre interface EXCEPT that
a) references to other Ogre types (as return or parameter types) should become the equivalent Adapter type (if you want to be able to mock the calls on that object)
b) all functions are virtual, even if the original Ogre one isnt'.

Your class under test uses the adapter interface. Adapters are constructed through an AdapterManager, passing the real Ogre object (though this would generally be done outside the class under test).
A "forwarding" implementation of this interface can be returned to run the real app. The forwarder is constructed with the real Ogre object and just passes calls on.

2) Define a mock subclass of the adapter, this is where Google Mock comes in with their mocking macros.
Your tests use this and pass it to the class under test. It means in tests you never have to create an actual Ogre object, avoiding the need to do any Ogre initialisation (like root, render systems etc).

3) By keeping the adapter interface identical, I'm hoping that I can use conditional compilation to replace the Adapter type with the real Ogre type. Will be used for deployment builds, removing all those virtual functions and forwarding.
I haven't actually tried this step yet...

It seems like a bit of work, but I only need to adapt the Ogre classes and function's I'm actually using in tested code, and it only needs to be done once.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Re: Mocking Ogre

Post by jacmoe »

Thanks for the info! :)
No, I haven't gotten 'round to doing anything yet, but I will (eventually).
I plan on mocking my rendering component to make it easier to test/debug/visualize my framework.
Sounds really neat being able to run your app in alternative ways, without the rendering component getting in the way. :wink:
wally wrote:does it avoid having to create real instances of Ogre objects? (Entity etc can be problematic to construct without some Ogre initialisation).
I think you need to mock all or nothing.
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
wally
Halfling
Posts: 89
Joined: Tue Apr 01, 2008 1:10 am
Location: Canberra, Australia

Re: Mocking Ogre

Post by wally »

I downloaded MockItNow and did a quick test: looks like it DOES allow you to use uninitialised (or fake) pointers and references to objects. The tests below ran fine and passed, also failed correctly when commenting out DoStuff call.

So I was all ready to say this looks far better than my solution, then I saw this on the MockItNow page:
Also, you need to make sure that the classes you use with the mocker have default constructors. This is to ensure that they can be returned during the record phase, and that virtual functions can be fixed up.
Tried this in my test (add protected default constructor on AnOgreObject, see below) and sure enough it no longer compiles.
Things like Entity have protected default constructors (maybe some have no default too?). So MockItNow can't mock them.

So I don't think this is going to work. :cry:

Other (small) downsides are:
- not sure how to do the same thing on other platforms (e.g. XCode / GCC on Mac OSX), but they indicate it is possible.
- requires special build settings when building Ogre (and anything else that gets mocked). The site mentions needing to disable
* Inlining
* Incremental linking
* COMDAT folding

My tests:

Code: Select all


class AnOgreObject
{
public:
	void DoStuff() const
	{
	}
protected:
	// This breaks the ability to mock this class
	//AnOgreObject() {}
};

class ObjectUser
{
public:
	void DoStuffWithObject( AnOgreObject* o )
	{
		o->DoStuff();
	}

	void DoStuffWithObjectRef( const AnOgreObject& o)
	{
		o.DoStuff();
	}
};

struct FixtureP
{
	FixtureP()
	{
	}

	ObjectUser testedObject;
	AnOgreObject* anUnInitialisedPointer;
	Mocker mocker;
};

TEST_FIXTURE (FixtureP, CanMockWithDummyPointers)
{
	REGISTER(AnOgreObject::DoStuff);

	RECORD
	{
		anUnInitialisedPointer->DoStuff();
	}

	testedObject.DoStuffWithObject( anUnInitialisedPointer );
}

TEST_FIXTURE (FixtureP, CanMockWithDummyRefs)
{
	REGISTER(AnOgreObject::DoStuff);

	RECORD
	{
		(*anUnInitialisedPointer).DoStuff();
	}

	testedObject.DoStuffWithObjectRef( (*anUnInitialisedPointer) );
}
Last edited by wally on Sun May 03, 2009 12:36 am, edited 1 time in total.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Re: Mocking Ogre

Post by jacmoe »

Ouch. This is bad news. I hoped I could get away with a lightweight mocking library.. :|
Well, well..
I'd love to hear about your experiences in Ogre mocking. :)

<edit>
Looked into Google Mock once more - and it's not that bad. And it seems to handle just about anything, including Ogre. :wink:
</edit>
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Re: Mocking Ogre

Post by jacmoe »

I found Mock Objects for C++ - it's very lightweight, and doesn't seem to have the same limitations as MockItNow has. :wink:
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
wally
Halfling
Posts: 89
Joined: Tue Apr 01, 2008 1:10 am
Location: Canberra, Australia

Re: Mocking Ogre

Post by wally »

I'm still plugging along with Google Mock. It seems to be going ok, though I've only tested a few simple classes so far. I'll post back more once I have a good idea how things generally need to be setup.
I took a very quick look at Mock Objects for C++. Maybe I didn't read far enough but it seems to be somewhat verbose. It could be that I've only seen the "flexible" usage, and there's a "common use" version further on.
wally
Halfling
Posts: 89
Joined: Tue Apr 01, 2008 1:10 am
Location: Canberra, Australia

Re: Mocking Ogre

Post by wally »

A "brief" report on using Google Mock to mock Ogre. (Sorry it's taken a while)
In summary: It's possible, fairly readable and I think worthwhile. It can be slightly painful to setup initially but you only need to define your Adapter and Mock classes once, and only the bits of the interface you're currently using.

First the aims:
1) Be able to run tests without any Ogre initialisation.
(once you start to initialise the real system, you quickly need some kind of renderer etc.)

2) Being able to create and pass any mocked Ogre object (such as an Entity) to the code under test.
(Using raw Ogre, this can be difficult due to non-public constructors and dependency on resources)

3) For a "final / deployment" build, be able to compile away any virtual calls that are only virtual for testing purposes.

4 Don't modify Ogre's own source code.

I defined an "Adapter" for every non-trivial Ogre class involved in tested code.
The adapter interface appears identical to the real Ogre interface from the app's point of view, except
a) every method is pure virtual (even those that are non-virtual in Ogre). This is required to be able to mock it (specifically to set expectations on the method).
b) Method parameters and return types need to be changed to the adapter version where applicable.

E.g. for SceneNode

Code: Select all

namespace OgreAdapters
{
	class SceneNode : public Node
	{
	public:
		virtual ~SceneNode() {}
		virtual void attachObject( MovableObject* obj) = 0;

		virtual SceneNode* createChildSceneNode( 
			const Ogre::Vector3& translate = Ogre::Vector3::ZERO, 
			const Ogre::Quaternion& rotate = Ogre::Quaternion::IDENTITY ) = 0;

		virtual SceneManager* getCreator(void) const = 0;
		...
	};
}
(note Node, MovableObject and SceneManager in above types refers to OgreAdapters type)

Then derive a mock class from this with Google Mock:

Code: Select all

namespace OgreMocks
{
	class MockSceneNode : public OgreAdapters::SceneNode
	{
	public:
		MOCK_METHOD1( attachObject, void ( OgreAdapters::MovableObject* obj) );
		MOCK_METHOD2( createChildSceneNode, SceneNode* ( const Ogre::Vector3& translate, const Ogre::Quaternion& rotate ) );
		MOCK_CONST_METHOD0( getCreator, OgreAdapters::SceneManager* (void) );
		...
	};
}
With this, a fairly simple test

Code: Select all

TEST_F( AxisGridNodeBuilderFixture, CreatesXAxisEntity )
{
	SetupNodeReturnSequence();
	SetupEntityReturnSequence();

	EXPECT_CALL( m_axisNodes[0], attachObject( &m_axisEntities[0] ) );
	EXPECT_CALL( m_axisNodes[0], setDirection( Vector3::UNIT_X, Ogre::Node::TS_LOCAL, _  ) );
	EXPECT_CALL( m_axisNodes[0], setScale( Vector3( kLateralScale, kLateralScale, kLengthScale ) ) );
	EXPECT_CALL( m_axisEntities[0], setMaterialName( xMaterial ) );

	m_builder.BuildNode( m_parentNode, kOffset );
}
Which uses this fixture:

Code: Select all

	class AxisGridNodeBuilderFixture : public testing::Test
	{
	public:
		AxisGridNodeBuilderFixture()
		{
			EXPECT_CALL( m_parentNode, getCreator() ).WillRepeatedly( Return( &m_sceneManager ) );
			m_builder.SetAxisMeshName( kMeshName );
			m_builder.SetMaterialForAxis( xMaterial, QSim::XAxis );
			...
		}

	protected:

		// Simpler if we assume a certain order: X,Y,Z
		void SetupNodeReturnSequence()
		{
			EXPECT_CALL( m_parentNode, createChildSceneNode( kOffset, _ ) )
				.Times( AtMost(3) )
				.WillOnce( Return( &m_axisNodes[0] ) )
				.WillOnce( Return( &m_axisNodes[1] ) )
				.WillOnce( Return( &m_axisNodes[2] ) );
		}

		void SetupEntityReturnSequence()
		{
			EXPECT_CALL( m_sceneManager, createEntity( _, kMeshName ) )
				.Times( AtMost(3) )
				.WillOnce( Return( &m_axisEntities[0] ) )
				.WillOnce( Return( &m_axisEntities[1] ) )
				.WillOnce( Return( &m_axisEntities[2] ) );
		}

		QSim::AxisGridNodeBuilder m_builder;
		OgreMocks::MockSceneNode m_parentNode;
		OgreMocks::MockSceneManager m_sceneManager;
		OgreMocks::MockMeshManager m_meshManager;

		// NiceMock eliminates warnings about "uninteresting calls",
		// i.e. calls which we didn't set expectation for as we don't care
		testing::NiceMock< OgreMocks::MockSceneNode > m_axisNodes[3];
		OgreMocks::MockEntity m_axisEntities[3];
	};
}
The above satisfies aims 1 & 2. We don't even need to create an Ogre::Root to run the tests.

One big assumption: You're willing to setup a diferent build config to build and run your tests, vs building your actual app. If you want to use the same build config, you'll need "forwarding" subclasses of the adapter classes that forward calls on to the real Ogre object when running your app.

For aim #3:
The code under test always uses the OgreAdapters type.
But a preprocessor flag can resolve this back to the raw ogre type:

Code: Select all

namespace OgreAdapters
{
	#ifdef ENABLE_OGRE_ADAPTERS
		#define DECLARE_OGRE_ADAPTER( className ) class className;
	#else
		#define DECLARE_OGRE_ADAPTER( className ) using Ogre::className;
	#endif // ENABLE_OGRE_ADAPTERS

	DECLARE_OGRE_ADAPTER( Entity );
	DECLARE_OGRE_ADAPTER( SceneNode );
	...
Some other details:
* For "trivial" Ogre classes (those that don't depend on other classes / resources and are easy to inspect) I just use the Ogre class directly rather than mocking it. E.g. Vector3
* Any mock of a singleton should implement the GetSingleton* methods for real
* Adapting a shared pointer requires something like

Code: Select all

namespace OgreAdapters
{
	class Mesh
	{
	public:
	...
	};

	class MeshPtr : public Ogre::SharedPtr< Mesh >
	{
	public:
		// other constructors not yet implemented
		MeshPtr( Mesh* rep ) : SharedPtr( rep ) {}
	};
}
At some point in you app code, where you move from untested code to tested code you need to start using the OgreAdapters type instead of the raw Ogre type.
I make this switch using an AdapterManager:

Code: Select all

namespace OgreAdapters
{
#ifdef ENABLE_OGRE_ADAPTERS

	class AdapterManager
	{
	public:
		static SceneManager* Adapt( Ogre::SceneManager* ogreObject );
		...
		// Not yet implemented: will only be needed if a testable build actually uses Adapt()
		static void DestroyAdapter( SceneManager* adapter );
		...
	};
#else //==> !ENABLE_OGRE_ADAPTERS

	class AdapterManager
	{
	public:
		static inline SceneManager* Adapt( Ogre::SceneManager* ogreObject ){ return ogreObject; }
		...
		// There was no separate adapter created, so nothing to destroy
		static inline void DestroyAdapter( SceneManager* adapter ) {}
		...		
(This is where you would have to return a "forwarding" version of the adapters if you choose that path)

If you want to look at the full code, it's available on the sourceforge project:
http://qsimulator.svn.sourceforge.net/v ... main/code/
(Revision 53 is known working build at present)
There are separate VS 2005 projects for adapters, mocks, tests. To build it requires a bit more setup, see the "rough" dev guide in the parallel "docs" folder. Basically requires Ogre, CEGUI, Google Mock (and its boost tr1/tuple dependency) and Google Test.