Implementing tests

Each method having test prefix is treated as a test case, which is executed separately by the framework. The test framework calls setUp() before calling the test method and tearDown() after the test case execution. In setup it is advisable to set up the test target to a default state. In test cases, it is only necessary to run methods from an already instantiated test target and verify that behavior and state is expected. Teardown is implemented to clean up the test target and other resources created during setup. You could implement the setup as follows:

void CMapExampleSmsEngineTest::setUp()
	{
	iObserver = new (ELeave) DummyObserver();
	iTarget = CMapExampleSmsEngine::NewL(iObserver);
	}

The engine requires an observer to be passed during construction. A dummy stub can be used (see details in TestSource.cpp) for the parameter. Teardown is implemented to free the resources:

void CMapExampleSmsEngineTest::tearDown()
	{
	delete iTarget;
	delete iObserver;
	}

A first real test case could be to test message sending. This implementation is fairly simple: the method is called and if it does not leave (throw exception), the test case is passed:

void CMapExampleSmsEngineTest::testSendMessage()
	{
	iTarget->SendSmsL(_L("12345678"), _L("abcd"));
	}

Message sending may also fail. To simulate exception, you can implement RSendAsMessage::SetBodyTextL() to leave. Then you can implement the test case to ensure SendSmsL to leave. However, SetBodyTextL shall leave only for this test case and thus its behavior shall be controllable from test case. See TestSource.cpp for details.

One way to achieve controllability is to use a global variable, which the test case sets before calling the test target, and then implement SetBodyTextL to behave based on the variable state. A more generic approach is to define a global function pointer, which SetBodyTextL will call if it is set. The stubbed code and test case would be something like this:

// global function pointer
void (*gRSendAsMessage_SetBodyTextLHook)() = NULL;

void ThrowExceptionL()
	{
	User::Leave(KErrGeneral);
	}

void RSendAsMessage::SetBodyTextL(const TDesC16& a)
	{
	if(gRSendAsMessage_SetBodyTextLHook)
		gRSendAsMessage_SetBodyTextLHook();
	}

void CMapExampleSmsEngineTest::testSendMessageExceptions()
	{
	gRSendAsMessage_SetBodyTextLHook = ThrowExceptionL;
	TS_ASSERT_THROWS_ANYTHING(
		iTarget->SendSmsL(_L("12345678"), _L("abcd"))
	);
}

The test case first sets the function pointer to refer to the function, which will leave. Then the test case calls SendSmsL. The call is capsulated within assert macro. That macro checks that the capsulated code will throw an exception. If it does not, it reports to the test framework that the test case did not pass.