|
From: Steve F. <sm...@us...> - 2001-09-24 23:40:25
|
Update of /cvsroot/mockobjects/mockobjects-java/doc/xdocs/papers
In directory usw-pr-cvs1:/tmp/cvs-serv30856
Added Files:
brief_introduction.html
Log Message:
early version
--- NEW FILE: brief_introduction.html ---
<html>
<head>
<title>Developing JDBC applications test-first</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style type="text/css">
<!--
.deemphasised { color: #666666}
h1 { text-align: center; font-family: Arial, Helvetica, sans-serif; font-weight: bold}
h3 { font-family: Arial, Helvetica, sans-serif; font-style: italic; font-weight: bold; font-size: small}
.inline_code { font-family: "Courier New", Courier, mono; font-style: normal; font-size: smaller; vertical-align: middle}
p { font-family: Arial, Helvetica, sans-serif}
li { font-family: Arial, Helvetica, sans-serif }
h2 { font-family: Arial, Helvetica, sans-serif; margin-top: 3%}
-->
</style>
</head>
<body bgcolor="#FFFFFF">
<h1>How Mock Objects happened</h1>
<p align="center">Steve Freeman <tt> <st...@m3...></tt></p>
<h2>Introduction</h2>
<p>Unit testing is a fundamental practice in Extreme Programming, but most nontrivial
code is difficult to test in isolation. You need to make sure that you test
one feature at a time, and you want to be notified as soon as any problem occurs.
Normal unit testing is hard because you are trying to test the code from outside.
</p>
<p>Mock Objects is an approach to writing unit tests based on two key techniques:
replace everything except the code under test with mock implementations that
emulate the rest of the environement, and put the test assertions <em>inside</em>
those mock implementations so that you can validate the interactions between
the objects in your test case.</p>
<h2>Simple unit tests</h2>
<p>Most people who start writing unit tests by calling some methods and checking
some of the state of the object under test. For example, a test for a simple
calculator object might be:</p>
<pre> public void setUp() {
this.calculator = new Calculator();
}
public void testAdd() {
this.calculator.set(5);
this.calculator.add(3);
assertEquals("Should be addition", 8, this.calculator.getResult());
}
</pre>
<p>As a class becomes more complex, this style of testing becomes harder to maintain.
For example, if our calculator now keeps a history of the calculations it makes,
a test might be:</p>
<pre> public void testHistory() {
this.calculator.set(5);
this.calculator.add(3);
Vector calcuations = new Vector();
calcuations.add(new Calculation(Calculation.SET, 5));
calcuations.add(new Calculation(Calculation.ADD, 3));
assertEquals("Should be equal calculations",
calcuations, this.calculator.getHistory());
}
</pre>
<p>The problem with this approach is that we must either return the actual history
data structure, which limits our choice of implementation, or return a vector
copy, which is expensive. Furthermore, it is impossible to tell which choice
we have made from the test. To hide the underlying collection, the next step
is to return an <span class="inline_code">Iterator</span> for the calculation
history, rather than the collection itself. Our test now becomes:</p>
<pre> public void testHistory() {
this.calculator.set(5);
this.calculator.add(3);
Vector calcuations = new Vector();
calcuations.add(new Calculation(Calculation.SET, 5));
calcuations.add(new Calculation(Calculation.ADD, 3));
compareIterators(calculations.iterator(), this.calculator.getHistory());
}</pre>
<p>where <span class="inline_code">compareIterators()</span> is a helper method
that compares the contents of two Iterators. </p>
<p>This approach becomes less manageable as the code becomes still more complex.
For example, we now change our class to perform some lengthy calculation that
takes multiple input values and returns multiple output values:</p>
<pre> public void testLongCalculation() {
Result[] results =
this.calculator.complicatedOperation(makeLongCalculationInputs());
compareResults(makeLongCalculationResults(), results);
}</pre>
<p>To manage the inputs and results, there are helper methods, <span class="inline_code">makeLongCalculationInputs</span>
and <span class="inline_code">makeLongCalculationResults</span>, for constructing
the collections of values. </p>
<p>Tests like this are effectively small-scale integration tests, they set up
pre-conditions and test post-conditions but they have no access to the code
while it is running. When this test fails, we will have to step through the
code to find the problem because the assertions are made <em>after</em> the
calculation is finished. Is there a better way?</p>
<h2>A first Mock Object</h2>
<p>One thing we know about this complicated operation is that we need to accumulate
a set of results that currently are returned from the method. We can factor
out that process of accumulation to a separate object, and write an interface
for it:</p>
<pre> public interface Accumulator {
/**<br> * called once for each result in the calculation
*/
void add(Result result);<br> } </pre>
<p> </p>
<hr>
<p>© Steve Freeman, 2001</p>
<p> </p>
</body>
</html>
|