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.