Home > Software Quality Tips > Software Testing > Using JMock in test-driven development
Software Quality Tips:
EMAIL THIS
 TIPS & NEWSLETTERS TOPICS 

SOFTWARE TESTING

Using JMock in test-driven development


Paulo Caroli
12.01.2006
Rating: -5.00- (out of 5)


Digg This!    StumbleUpon Toolbar StumbleUpon    Bookmark with Delicious Del.icio.us   


Applying mock objects effectively is a key factor when performing test-driven development (TDD). In this article I'll introduce the basics of using JMock, a mock object framework, in conjunction with test-driven development. To illustrate the technique I will work through a case study, the creation of a cache component by means of test-first development with JMock.

Unit test and Mock Objects

Soon after the creation of the first unit test frameworks, a common need was identified and a complementary initiative began – mock objects. Mock objects help you design and test the relations between the objects entangling the whole system. You can think of mock objects as stubs that mimic the behavior of real objects in controlled ways. The process of identifying mock objects during test creation helps on defining objects interfaces. Thus developing with mock objects helps on building a loosely coupled - and therefore maintainable, reusable, and testable – system.

Typically an object-oriented (OO) application consists of a network of objects working together to accomplish a goal. Unit testing OO applications is a difficult task, especially when dealing with dependencies among objects. Figure 1 shows a hypothetical OO system.

[IMAGE]

Figure 1: A network of objects

Let’s analyze the effort of unit testing component A. For this purpose, Figure 2 attempts to create a visual representation for the Unit Test of component A – ATest. Such visual representation illustrates that ATest’s boundaries go beyond component A; somehow ATest also depends on components B and C.

[IMAGE]

Figure 2: ATest unit test

In an attentive change of ATest, let’s consider adding components B and C into the ATest unit test. Figure 3 shows a new version of ATest where its boundaries have been extended


Digg This!    StumbleUpon Toolbar StumbleUpon    Bookmark with Delicious Del.icio.us   


RELATED CONTENT
Test-driven development (TDD)
Agile development growing, but problems remain
The challenges of test-driven development (TDD)
Agile and waterfall neck and neck as business side fails to engage
Big Blue dog learns new tricks: How IBM Software Group moved to agile
Clean Code: A Handbook of Agile Software Craftsmanship, Chapter 1 -- What Is Clean Code?
Successful test-driven development (TDD) with external systems
Improved software design with test-driven development (TDD)
Test-driven development and the ethics of quality
Continuous integration meets application performance management
Can test-driven development drive developers to test?

Software unit testing
Software testing deliverables: Developing a software testing strategy
Evaluating the benefits of automated software testing
Adopting continuous integration brings agility, other benefits
Tools, standards address persistent quality assurance (QA) issues
Increasing productivity with unit testing
Don't write simplistic test cases
How to develop a checklist for unit, integration and system testing
Making unit testing a priority
The Art of Debugging with GDB, DDD, and Eclipse -- Ch. 1
An approach to integration testing

Software Testing
10 steps to acing Web app security assessments
Three software regression testing steps can perfect defect fixes
Exploring mobile layout testing, emulators and goals
Preparing for testing applications in the cloud
Hack maliciously to boost your software's security
Testing functionality, performance of mobile Web applications
Testing mobile Web applications for usability and context
Using SBTM for exploratory testing coverage problems
Web 2.0, RIAs push load testing to the max
Using session-based test management for exploratory testing

RELATED GLOSSARY TERMS
Terms from Whatis.com − the technology online dictionary
continuous integration  (SearchSoftwareQuality.com)
JUnit  (SearchSoftwareQuality.com)
NUnit  (SearchSoftwareQuality.com)
test-driven development  (SearchSoftwareQuality.com)

RELATED RESOURCES
2020software.com, trial software downloads for accounting software, ERP software, CRM software and business software systems
Search Bitpipe.com for the latest white papers and business webcasts
Whatis.com, the online computer dictionary


to components B and C.

[IMAGE]

Figure 3: ATest adding B and C

The new version of ATest, as depicted in Figure 3, adds B and C into ATest unit test. But once again ATest has more dependencies. Component C depends on component D. And the dependency chain could go further (D depends on E). The hypothetical ATest inclusive expansion would create a unit testing component A and all subsequent dependencies. Basically ATest should test component A only. It should be tangible and concise. A recommended alternative solution for ATest is to use stubs - mocks - of component B and C (Figure 4) instead of the real implementations of B and C.

[IMAGE]

Figure 4: ATest with mockB and mockC

As depicted in Figure 4, ATest does not include D – or D’s mock. As far as ATest is concerned A uses B and C. Therefore mock B and mock C are enough for effectively building ATest unit test.

Mock objects help isolate the component being tested from the components it depends on. It does so by providing a mechanism to create stubs of components and its expected behavior. This mechanism relies on the dependent components interface. By doing TDD with mock objects you will produce components which are independent of each other implementation. Instead components will dependent on each other’ interface. Figure 4 shows the representative replacement of components B and C by its equivalent mock B and mock C.

TDD

Test-driven development (TDD) is an evolutionary approach to development which instructs you to have test-first development intent. Basically, you start by writing a test and then you code to elegantly fulfill the test requirements.

The steps of TDD are overviewed in “The Steps of test-first development.” The first step is to quickly add or expand a unit test to your test suite. Next you run the test suite to ensure that the new test does in fact fail. You then update your functional code until it passes the test suite. Once the test suite does not fail, you should refactor the code; and then start over again.

[IMAGE]

Figure 5: TDD Steps

Refactoring is a development practice for restructuring an existing code, altering its internal structure without changing its external behavior. Refactoring keeps the code clean and easy to understand. Refactoring is safer in TDD because you always have the test required for validating the component, independently of its internal implementation. A good time for refactoring is after a test pass state, thus you can refactor and verify that the test is still passing.

Case study

The case study consists of following the TDD steps (as presented in Figure 5 ) for a cache component creation. If you are new to TDD and JMock you should follow this article linearly and make the presented code work in your development environment. Make sure to set-up your development environment with JUnit and JMock libraries. A good IDE can be of great help, especially in a TDD approach.

In the Mock Roles not Objects (PDF) article, the authors use a staged timed cache development to explain the article main concepts. The cache development is a very instructive example for introducing mock objects. But moreover, it provides a great sample for applying TDD. For these reasons, in this article, I also use the cache code as the basis of the case study. Whereas I present a new implementation for the TimedCache, and apply TDD to demonstrate each interaction until the development is complete. At the end of this case study you will have implemented TimedCache, its test component and the interfaces of the services used by the cache.

The case study is separated in 3 interactions following the TDD steps (figure 5) in the proposed order:

Interaction 1

Requirement

The cache component must load an object when lookUp() is invoked for the first time.

1.1 test code

TimedCacheTest, a unit test for TimedCache, is created prior to any functional code development – test first approach.

TimedCacheTest class

Code Notes:

The cache development should not be dependent of the ObjectLoader code development nor its internal implementation details. TimedCacheTest unit test, as shown in Figure 6 , uses, a mockLoader to stub the ObjectLoader functionality. Applying mock objects is a great way to achieve loose coupling and identifying interface during your development. For example ObjectLoader interface has been identified while developing TimedCacheTest

[IMAGE]

Figure 6: TimedCacheTest unit test

The cache component takes the ObjectLoader dependency in its constructor on instantiation. This design is known Construction Dependency Injection (CDI). The Construction Dependency Injection makes possible for the cache to use an ObjectLoader component (a real implementation or a Mock object) without depending on its concrete implementation.

Test Results

<< TimedCacheTest.testLoadsObjectThatIsNotCached fails>>

The test code does not compile.

1.2 functional code

Following are the code necessary for fulfilling the test.

ObjectLoader interface

Code Notes:

TimedCache class

TimedCache class is created with the simplest implementation that makes the test pass:

Code Notes:

Test Results

<< TimedCacheTest.testLoadsObjectThatIsNotCached pass>>

1.3 Refactor

Following is a refactored version of the TimedCacheTest refactored.

Code Notes:

Test Results

<< TimedCacheTest.testLoadsObjectThatIsNotCached pass>>

Interaction 2

Requirement

The cache component must load an object when lookup() is invoked for the first time.

The cache component must not reload objects that have been previously loaded.

2.1 test code

A new test for the presented requirement has been added to the test class.

TimedCacheTest class

Code Notes:

Test Results

<< TimedCacheTest.testLoadsObjectThatIsNotCached pass>>

<< TimedCacheTest.testDoesNotLoadObjectThatIsCached fails >>

The current cache implementation version does invoke the loader for each lookup() call. This is caught in the new unit test method - testDoesNotLoadObjectThatIsCached () . Basically the JMock expectation on mockLoader.load() method fails as it is invoked twice instead of once.

2.2 functional code

TimedCache class :

The following code enhances the TimedCache functionality, adding caching
capabilities, so an Object, if previously loaded, will not be reloaded.

Code Notes:

Test Results

<< TimedCacheTest.testLoadsObjectThatIsNotCached pass>>

<< TimedCacheTest.testDoesNotLoadObjectThatIsCached pass>>

Interaction 3

Requirement

The cache component must load an object when lookup() is invoked for the first time.

The cache component must not reload objects that have been previously loaded and have been on cache for too long.

The cache component must reload objects that have been previously loaded and have not been on cache for too long.

3.1 test code

A new method for the new requirement has been added to the unit test code.

The new requirement introduces the aspect of time, time elapsed, and a decision to determine if an object should be reloaded. A point in time is represented by Timestamp object. The ReloadPolicy component is responsible for deciding upon Objects reload.

Below you will find the modified code of the TimedCacheTest class.

TimedCacheTest class

Code Notes:

Code Notes:

Code Notes:

Figure 7 represents the unit test and the cache component using mockLoader, mockReloadPolicy and mockClock, which respectively stub the functionalities for ObjectLoader, ReloadPolicy and Clock.

[IMAGE]

Figure 7: TimedCachetTest unit test and required mock objects

Test Results

<< TimedCacheTest.testLoadsObjectThatIsNotCached fails>>

<< TimedCacheTest.testDoesNotLoadObjectThatIsCached fails >>

<< TimedCacheTest.testCachedObjectsAreNotReloadedBeforeTimeout fails >>

The test code does not compile because of the new changes.

3.2 functional code

Following are the code necessary for fulfilling the new test.

Clock interface

Code Notes:

ReloadPolicy interface

Code Notes:

TimedCache class

The following code segments enhance the TimedCache component, making the necessary changes for fulfilling the new requirement.

Code Notes:

Code Notes:

Code Notes:

Test Results

<< TimedCacheTest.testLoadsObjectThatIsNotCached fails>>

<< TimedCacheTest.testDoesNotLoadObjectThatIsCached fails >>

<< TimedCacheTest. testCachedObjectsAreNotReloadedBeforeTimeout pass >>

Oops, the new test passed, but the previous ones failed.

Why did this happen?

The TimedCache.lookUp() method implementation has changed. Clock and ReloadPolicy are now being invoked.

JMock complains about any method invocation for which you haven't accounted for in advance with an expectation. Basically the previous tests have not been prepared for clock.getCurrentTime() and reloadPolicy.shouldReload() invocations.

TimedCacheTest class

Below is the code update for testLoadsObjectThatIsNotCached and testLoadsObjectIsNotCached unit test methods in order to set expectations correctly.

Code Notes:

Test Results

<< TimedCacheTest.testLoadsObjectThatIsNotCached pass >>

<< TimedCacheTest.testDoesNotLoadObjectThatIsCached pass >>

<< TimedCacheTest.testCachedObjectsAreNotReloadedBeforeTimeout pass >>

3.3 Refactor

Once again all test scenarios are passing.

The last test – testCachedObjectsAreNotReloadedBeforeTimeout – ensures that objects are not reloaded if they have not timed out according to the reload policy. To make sure the cache and its reload policy are working properly we still need to test the case where objects that have timed out will be reloaded.

The following code adds this test case to TimedCacheTest class.

TimedCacheTest class

Code Notes:

Test Results

<< TimedCacheTest.testLoadsObjectThatIsNotCached pass>>

<< TimedCacheTest.testDoesNotLoadObjectThatIsCached pass >>

<< TimedCacheTest.testCachedObjectsAreNotReloadedBeforeTimeout pass >>

<< TimedCacheTest.testReloadsCachedObjectAfterTimeout pass >>

Conclusion

This article detailed a case study using mock objects in test-driven development. It included a full implementation, in a test-first style, of a cache component and its test case. You leveraged how mock objects are able to stub dependent components in a test code.. Furthermore, you observed the creation of a fully-tested, loosely coupled system with well defined interfaces as a result of following a staged TDD in conjunction with mock objects. The case study in this article uses Java, JUnit and JMock, but you can employ the same concepts and steps with other programming languages, unit tests and Mock object frameworks.

Paulo Caroli, www.caroli.org, is an application architect for ThoughtWorks, an outstanding application development and consulting company. Paulo has a master's degree in software engineering and is a Sun Certified Architect for Java Technology, but moreover, following ThoughtWorks leadership, has been successfully applying Agile techniques such as XP and TDD.

I would like to thank ThoughtWorks and its employees for their shared knowledge and practical Agile expertise, and especially offer my thanks to Paul Hammant, www.paulhammant.com, my mentor and co-worker at ThoughtWorks for reviewing this article.

This article originally appeared on TheServerSide.com.

Rate this Tip
To rate tips, you must be a member of SearchSoftwareQuality.com.
Register now to start rating these tips. Log in if you are already a member.




DISCLAIMER: Our Tips Exchange is a forum for you to share technical advice and expertise with your peers and to learn from other enterprise IT professionals. TechTarget provides the infrastructure to facilitate this sharing of information. However, we cannot guarantee the accuracy or validity of the material submitted. You agree that your use of the Ask The Expert services and your reliance on any questions, answers, information or other materials received through this Web site is at your own risk.



Software Design & Testing - Project Management
About Us  |  Contact Us  |  For Advertisers  |  For Business Partners  |  Site Index  |  RSS
SEARCH 
TechTarget provides technology professionals with the information they need to perform their jobs - from developing strategy, to making cost-effective purchase decisions and managing their organizations' technology projects - with its network of technology-specific websites, events and online magazines.

TechTarget Corporate Web Site  |  Media Kits  |  Site Map




All Rights Reserved, Copyright 2006 - 2009, TechTarget | Read our Privacy Policy
  TechTarget - The IT Media ROI Experts