Vous êtes sur la page 1sur 6

2009 Sixth International Conference on Information Technology: New Generations

Interface-Based Object-Oriented Design with Mock Objects

Jagadeesh Nandigam1, Venkat N Gudivada2, Abdelwahab Hamou-Lhadj3 and Yonglei Tao1


1 2 3
Computing & Inf. Systems Engr. & Computer Science Electrical & Computer Engr.
Grand Valley State University Marshall University Concordia University
Allendale, MI, USA Huntington, WV, USA Montreal, Quebec, Canada
{nandigaj,taoy}@gvsu.edu gudivada@marshall.edu abdelw@ece.concordia.ca

Abstract oriented system design seems to encourage and enforce


the development of interface-based systems. This paper
Interfaces are fundamental in object-oriented systems. describes our experiences in using mock objects to teach
One of the principles of reusable object-oriented design, interface-based design.
according to Gamma et al., is program to an interface, not
an implementation. Interface-based systems display three The rest of the paper is organized as follows. Section 2
key characteristics – flexibility, extensibility, and describes what interfaces are and how to define them in
pluggability. Designing with interfaces is therefore a Java programming language. In Section 3, we describe
better way of building object-oriented systems. Getting interface-based design and its benefits. Section 4 raises
students in introductory software engineering and design some issues in teaching interface-based design and why it
courses to program to interfaces and develop interface- should be enforced in introductory software engineering
based systems is a challenge. This paper presents our and design courses. Section 5 provides a brief
experiences with the use of mock objects to promote introduction to mock objects and the EasyMock tool.
interface-based design and effective unit testing in Section 6 describes how mock objects can be used as a
software engineering and design courses. teaching tool to teach interface-based design and require
that students develop interface-rich systems. Section 7
Keywords: interface-based design, mock objects, unit provides concluding remarks.
testing, interfaces, composition.
2. Interfaces

1. Introduction In object-oriented programs, one object (a client object)


interacts with another object through a set of operations
Interfaces are fundamental in object-oriented systems. An that can be sent to that object. This set of operations is
object’s interface is defined as the set of requests or called the interface of that object. In object-oriented
messages that can be sent to that object. The interface of programming languages, an interface is a language
an object does not contain implementation details. An construct that specifies the expected behavior of an object
object’s implementation is defined by its class. One of the as a set of operation/method signatures. A method
principles of reusable object-oriented design, according to signature usually includes the method name, the number,
Gamma et al. [3], is program to an interface, not an order, and type of its parameters, and its return type. An
implementation. Interface-based systems display three interface does not specify implementation details of
key characteristics – flexibility, extensibility, and operations defined in it. Interfaces are strictly
pluggability [2]. Interface-based design is therefore a specification-only constructs and say nothing about its
better way of building object-oriented systems. implementation. Different objects (as defined by their
implementing classes) are free to implement the
However, getting students in introductory software operations specified in an interface differently. As
engineering and design courses to program to interfaces interfaces are specification-only constructs, they are
and develop interface-based systems is a challenge. implicitly abstract and cannot be instantiated. Interfaces
Students seem to understand the role and benefits of allow a class to say that it has some particular behavior.
interfaces, yet they do not actively define and use An interface is similar to an abstract class, but without the
interfaces in object-oriented systems they develop, unless requirement that there be a parent/child relationship.
enforced by the instructor. The use of mock objects and
mock-object generators as required elements of object-

978-0-7695-3596-8/09 $25.00 © 2009 IEEE 713


DOI 10.1109/ITNG.2009.268
In the rest of the paper, we will use Java programming interfaces, you can decouple classes in your system.
language in the code examples. In Java, interfaces are According to Gamma et al. [3], there are two benefits to
defined using the keyword interface and may contain manipulating objects solely in terms of the interface
only method signatures and constant declarations [6]. An defined: i) clients remain unaware of the specific types of
interface can never contain method implementations. All objects they use as long as those objects provide the
methods declared in an interface are implicitly public interface that clients expect, and ii) clients remain
and abstract, even if these modifiers are omitted. All unaware of the classes that implement these objects.
constants declared in an interface are implicitly public,
static, and final, so these modifiers can be omitted. In interface-based design, you try to reduce the
The following is an interface definition in Java: implementation dependencies between classes and
subsystems by adhering to the following principle of
public interface IStack { reusable object-oriented design [3]: program to an
boolean empty(); interface, not an implementation. How do we abide by
Object peek(); this principle? The best way to do this is – i) make sure
Object pop(); method signatures have interface or abstract types as
void push(Object item); method parameters and return types, and ii) additionally,
int size(); do not declare class member variables to be instances of
} particular concrete classes. In general, do not commit to
concrete implementations, but only to an interface or a
A class, which is a blue-print for behavior of objects generic abstract type.
instantiated from it, uses the keyword implements to
specify that it implements the methods specified in a The benefits to adhering to the principle of “program to
given interface. A class may implement multiple an interface, not an implementation” are that the resulting
interfaces there by advertising to the rest of the world that system has three important quality characteristics [2]:
it has multiples sets of expected behavior. If a class flexibility (gracefully accommodating changes in
implements only some of the methods promised in an direction), extensibility (gracefully accommodating add-
interface it implements, then that class must be declared ons), and pluggability (gracefully substituting objects that
as an abstract class. The following Stack class have identical interfaces for each other at run-time using
implements the interface IStack: dynamic binding and polymorphism). According to
Gamma et al. [3], this principle is really about carefully
public class Stack implements IStack { managing dependency relationships in a large application.
public boolean empty() { It is easy to establish a dependency on a concrete class,
// implementation here but getting rid of an unwanted dependency later in the
} development may require significant amount of
Object peek(){ refactoring [4] work. Depending on concrete
// implementation here implementations may also be detrimental to reusing the
} code in another context. By depending on interfaces, you
Object pop(){ are decoupled from implementation. When you program
// implementation here to interfaces, you can vary implementation.
}
void push(Object item) { When you program to interfaces, you can successfully
// implementation here compile your code without having all the concrete classes
} that you require. However, in order to execute and test
int size() { your code, you will eventually need concrete classes that
// implementation here can be used as parameters and return value of methods
} and class member variables. For unit testing purposes, one
} can use mock objects (implementing the required
interfaces) in place of real objects. For deployment
3. Interface-Based Design purposes, one needs to have real, concrete classes that
implement the required interfaces. To summarize,
Interface-based (or interface-centric) design is a way of implementing to an interface is simply a better way of
developing object-oriented systems where one programming object-oriented systems.
consciously and proactively defines and uses interfaces
wherever possible in a design to reap the benefits of
designing with interfaces [1, 2]. With careful use of

714
4. Issues in Teaching Interface-Based Design object under test. They are lightweight, controllable
replacements for real objects [7-11]. Using mock objects,
Teaching object-oriented design in software engineering one can test an object in true isolation. Meszaros [14]
and design courses with focus on interfaces is very defines mocks as objects that are “pre-programmed with
important for reasons discussed earlier. Students learn to expectations which form a specification of the calls they
develop systems that are loosely coupled and have less are expected to receive. They can throw an exception if
implementation dependencies. The resulting systems are they receive a call they don't expect and are checked
more flexible, extensible, and pluggable [2]. Students can during verification to ensure they got all the calls they
learn to focus on interfaces to objects they need in their were expecting”.
solution without bogged down with the implementation
details. Astels [1] lists several uses for mock objects that go
beyond using them as replacement objects for
However, there are challenges to getting students to collaborators of an object during unit testing. The uses of
successfully define and use interfaces in their projects. mocks that are most relevant to us are – mocks for
There are several reasons for this. In general CS1 and nonexistent classes or to defer implementation to later
CS2 courses do not cover interfaces to sufficient depth for time, promoting interface-based design, refining
students to truly appreciate their use and the benefits of interfaces, encouraging composition over inheritance with
programming to interfaces. Students learn about interfaces, and unit testing in isolation from the rest of the
interfaces superficially on a need-basis in CS1 and CS2 system. Mocks are easiest to use if you adopt interface-
courses mostly during the following cases: when learning based design and adhere to the principle of program to an
about event listener interfaces and event listener classes interface, not an implementation. If we don’t depend on
they have to implement in GUI programming, when concrete implementations and commit to interfaces
implementing the java.lang.Comparable and instead, we can substitute a mock object for a real object
java.util.Comparator interfaces, and when dealing with as long as the mock object provides the same interface as
the Collections Framework in Java. the real object. Next section in the paper describes how
we can get students to observe interface-based design by
Students in introductory software engineering and design requiring students to use mock objects for unit testing. It
courses will have to be taught and even required to define is not that developing interface-based design depends on
and use interfaces in systems they design to build mocks, but using mocks in software development forces
interface-based systems. One way to enforce interface- one to develop code that is interface-based, loosely
based design is to require the use of mock objects for unit coupled, and where object composition via interfaces is
testing each class in true isolation. Use of mock objects favored. These benefits are reason enough to require the
forces the students to design, from up-front, each class use of mock objects in software engineering and design
where methods (including constructors) operate on courses.
interfaces instead of concrete classes. The resulting design
will automatically be interface-rich and unit testing In the rest of this section, we describe how to use
classes in isolation becomes possible. As will be seen EasyMock and JUnit to write unit tests for
later, the use of mock objects also addresses another DecimalToBinary class that has one collaborator, an
important principle [2,3] of reusable object-oriented object that implements the IStack interface. EasyMock is
design – Design with composition, rather than a library that provides an easy to use API to generate
inheritance. Mock objects and mock-object generators are mock objects for given interfaces [12]. Using EasyMock,
discussed next. you can generate mock objects for collaborators of an
object under test (called domain code) and associate the
5. Mock Objects mock objects with domain code. JUnit is a testing
framework to write and run repeatable unit tests [13].
An object-oriented program is a web of collaborating
The UML [5] class diagram in Figure 1 shows the classes
objects that send messages to each other to accomplish
involved in the decimal to binary number conversion
something [9]. In most non-trivial code, any given object
program and their relationships. The convert() method in
may rely on several other objects that are its collaborators.
the DecimalToBinary class takes one integer value, uses a
In unit testing, the goal is to test a class in true isolation.
stack object (real or mock) to convert an integer to a
However, it is hard to test an object that has many
corresponding binary number, and returns a string
collaborators in true isolation. At the same time, it is often
representing that binary value. The required collaborator
difficult, inefficient, or even impractical to instantiate the
object (i.e., an object implementing the IStack interface)
collaborators of a class under test. A mock object is a
is passed in as a parameter to the constructor in the
replacement for a real object that is a collaborator of the
DecimalToBinary class. It is important to note that the

715
collaborators of a class under test (DecimalToBinary, in @Before
our case) are explicitly passed in as parameters to either to public void setUp() {
the constructor or relevant set methods. mock = (IStack<Integer>)
createMock(IStack.class); // 1
In EasyMock, mock objects are generated dynamically at decToBin = new
runtime. It features a “record and replay” style of usage. DecimalToBinary(mock); // 2
In the record state (before calling replay), the mock object }
does not behave like a mock object, but it simply records
expected method calls and return values. During the
replay mode, it behaves like a mock object, checking @Test // a JUnit test case method
whether expected method calls are really made. The public void testConvert() {
common coding style or usage pattern for working with try {
mock objects is: 1) create a mock object for the desired checkOrder(mock,true);
interface, 2) associate the mock object with the domain // set expected calls – step 3
object as a parameter either to the constructor or a mock.push(1);
relevant set method, 3) specify expected calls and expectLastCall().times(3);
corresponding return values (during this stage, the mock expect(mock.empty()).andReturn(
object simply records expected calls), 4) switch the mock false);
object into replay mode, 5) exercise the functionality of expect(mock.pop()).andReturn(1);
domain code (the unit under test), 6) verify expected expect(mock.empty()).andReturn(
result of the domain code by asserting any postconditions, false);
and 7) finally verify that the mock object was used expect(mock.pop()).andReturn(1);
correctly. expect(mock.empty()).andReturn(
false);
expect(mock.pop()).andReturn(1);
expect(mock.empty()).andReturn(
true);
replay(mock); // 4
String actualResult =
decToBin.convert(7); // 5
assertEquals("111",
actualResult); // 6
verify(mock); // 7
} catch (EmptyStackException ex){}
}

In the setUp() method, we created a mock stack (step 1)


and created an instance of DecimalToBinary class with
the mock object as the parameter to the constructor (step
2). The mock stack is the collaborator of the
DecimalToBinary, the object that we want to test. Then,
Figure 1. Class diagram for DecimalToBinary Program we set expected method calls on the mock object by the
DecimalToBinary object (step 3). After setting expected
The DecimalToBinaryTest class is a JUnit test class that calls along with corresponding return values, we switched
uses a mocked stack (referred to as MockStack in Figure the mock object into replay mode (step 4). Now, we are
1) generated with the EasyMock tool and uses this as a ready to test the convert() method of DecimalToBinary
substitute for the real stack object while unit testing the object. We called the convert() method with an argument
DecimalToBinary class. The DecimalToBinaryTest class of 7 (step 5) and asserted that the actual result is equal to
contains a set of test cases methods for the the expected result “111” (step 6). Finally, we verified
DecimalToBinary (the object under test) class. The that the mock stack received the calls as expected (step 7).
setUp() method is a method that is run by the JUnit The use of mock objects allowed us to test the convert()
framework before every test case method to set the method and also how the DecimalToBinary object
required context for a test case. The testConvert() is a test interacted with its collaborator, the mock stack, without
case method that exercises the convert() method in the actually having the real stack object/class.
DecimalToBinary class with an input value of 7 and with
an expected binary string value of “111”.

716
The EasyMock tool provides many other features to implementation details, define and refine these interfaces
create mocks, set expected calls and returns values, and before building concrete classes, and continue to make
verify the expected behavior. The EasyMock tool progress in system development even when the required
documentation [12] provides detailed information on how concrete classes are not available yet. This might be the
to create and use mock objects [12]. case when the required class is being developed by
another student in a team-based project. Students working
6. Teaching Interface-Based Design with with interfaces will be able to make sufficient progress
Mock Objects and get their code to compile using interfaces without the
need for the actual classes. Of course, when it is time to
This section describes how the use of mock objects execute, one must have the concrete classes that
encourages (or requires) students to develop interface- implement the required interface(s). By enforcing these
based systems. According to Gamma et al. [3], a good two design constraints, the student teams effectively
object-oriented design adheres to two key principles – 1) develop software that adheres to the key object-oriented
program to an interface, not an implementation, and 2) design principle – program to an interface, not an
favor object composition over class inheritance. A design implementation.
process that observes these two principles yields object-
oriented designs that are loosely coupled, flexible, Design requirement 3 forces students to use object
extensible, and pluggable and promotes black-box reuse. composition and delegation as means for reusing existing
Now the challenge is how to get students to actively functionality. The resulting software will adhere to the
employ these principles while designing an object- other key object-oriented principle – favor object
oriented system? composition over class inheritance. In addition, the design
requirement 3 observes the Law of Demeter by explicitly
One approach to getting students to observe these passing in the collaborating objects when an object is
principles while designing is to specify few simple design created or immediately after. This is really required when
requirements (constraints) by the instructor as described working with mock objects because a mocked
below and strictly enforce these requirements during implementation can be substituted for the neighboring
review and project evaluations. (collaborating) objects for the object under test.

Since every object takes its collaborating objects


• Design Requirement 1: Students must prepare
explicitly as parameters to a constructor or other setter
interface specification for any concrete class that they
methods, the object is created at runtime using
(eventually) plan to have in the proposed system.
composition. Since the collaborators for an object are
Here, students give careful thought to interface of
specified as interfaces (see design requirement 2 above),
objects that they want without first worrying too
the resulting object is composed of objects that implement
much about specific implementation details.
an interface, rather than hardwired to a specific kind of
• Design Requirement 2: Students must make sure the
object. Designing with composition (especially when
parameters and return types of method they write,
based on interfaces) yields systems that are flexible,
and member variables they define in classes use
extensible, and pluggable [2]. We can now easily
interfaces instead of concrete classes.
substitute mock objects in place of real objects. Mocks
• Design Requirement 3: Everything that an object provide lightweight controllable replacements for the real
needs (i.e., the objects that it collaborates with) is objects. This is very useful when the real object is being
passed into it, either in the constructor or with developed by another member in the team and is not
appropriate setter methods. The resulting code tends available when a student is ready to test his/her object. In
to conform to the Law of Demeter [15] that addition, unit testing in true isolation is now possible.
informally states that every object is designed to Well tested units are relatively easy to integrate and create
work with only a limited set of objects (collaborators) fewer problems during integration testing. Design
that are explicitly passed in when the object is created requirement 4 depends on the outcome of the first three
or using a setter method. design requirements to make unit testing in isolation
• Design Requirement 4: Student must write unit tests possible.
using a unit testing framework like JUnit. If an object
under test requires one or more collaborating objects, The use of mock objects, therefore, promotes the
these objects must be replaced with mock objects in development of interface-based (or interface-centric)
order to test the domain object in true isolation. designs by requiring active use of interfaces in place of
concrete classes, favoring object composition with
Design requirements 1 and 2 together force students to interfaces, and making unit testing in true isolation
think about behavioral specifications before possible. These are all good design and testing techniques

717
and principles that should be taught in every software 11. Dave Thomas and Andy Hunt, Mock Objects, IEEE
engineering curriculum, especially in introductory courses Software, May/June 2002.
in software engineering and design. 12. EasyMock 2.4 tool and documentation, available
from http://easymock.org/
7. Conclusions 13. JUnit 4.x tool and documentation, from
http://junit.org/
In this paper, we attempted to show how mock objects 14. Gerard Meszaros, xUnit Test Patterns: Refactoring
can be used to enforce the development of interface-based Test Code, Addison-Wesley, 2007, ISBN
system in software engineering and design courses. A 0131495054.
good object-oriented design will adhere to two key 15. Karl Lieberherr and Ian Holland, Assuring Good
principles – program to interfaces instead of concrete Style for Object-Oriented Programs, IEEE Software,
classes, and favor object composition over class September 1989, pp 38-38.
inheritance. The role of unit testing is also critical to the
development of quality code. By using mock objects,
students can not only test their units in isolation, but also
develop code that adheres to the two key principles of
reusable object-oriented design mentioned above.
Therefore, it is important to teach interface-based design
in software engineering courses and mock objects provide
an indirect path to that end result.

8. References
1. David Astels, Test-Driven Development – A Practical
Guide, The Coad Series, Prentice Hall, 2003, ISBN
0-13-101649-0.
2. Peter Coad, Mark Mayfield, and Jonathan Kern, Java
Design: Building Better Apps and Applets, Yourdon
Press Computing Series, Prentice Hall, 2nd edition,
1999, ISBN 0-139-11181-6.
3. Erich Gamma, Richard Helm, Ralph Johnson, and
John Vlissides, Design Patterns – Elements of
Reusable Object-Oriented Software, Addison
Wesley, 1995, ISBN 0-201-63361-2.
4. Martin Fowler, Refactoring – Improving the Design
of Existing Code, Addison Wesley, 1999, ISBN 0-
201-48567.
5. Grady Booch, James Rumbaugh, and Ivar Jacobson,
The Unified Modeling Language User’s Guide,
Addison Wesley, 1999, ISBN 0-201-57168-4.
6. Peter van der Linden, Just Java 2, Sun Microsystems
Press, Sixth edition, 2004, ISBN 0-13-148211-4.
7. Alexander Chaffee and William Pietri, Unit testing
with mock objects, IBM developerWorks, November
2002.
8. Tammo Freese, EasyMock: Dynamic Mock Objects
for JUnit, http://www.agilealliance.org/system/
article/file/975/file.pdf
9. Steve Freeman, Tim Mackinnon, Nat Pryce, and Joe
Walnes, Mock Roles, Not Objects, available from
http://mockobjects.com/
10. Tim Mackinnon, Steve Freeman, and Philip Craig,
Endo-Testing: Unit Testing with Mock Objects,
available from http://mockobjects.com/

718

Vous aimerez peut-être aussi