File: improving_design_tutorial.xml

package info (click to toggle)
postfixadmin 2.3.5-2%2Bdeb7u1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 6,200 kB
  • sloc: php: 25,767; xml: 14,485; perl: 964; sh: 664; python: 169; makefile: 84
file content (197 lines) | stat: -rw-r--r-- 7,872 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
<?xml version="1.0"?>
<page title="Top down and test driven" here="Top down testing">
    <long_title>
        PHP unit testing tutorial - Top down design
        test first with mock objects
    </long_title>
    <content>
        <p>
            <a class="target" name="mock"><h2>Mock now, code later</h2></a>
        </p>
        <p>
            I lied.
        </p>
        <p>
            I haven&apos;t created a writer test at all, only the
            <code>FileWriter</code> interface that I showed
            earlier.
            In fact I&apos;ll go one step further away from a finished article and assume
            only an abstract writer in <em>classes/writer.php</em>...
<php><![CDATA[
<?php
    class <strong>Writer</strong> {
        
        function <strong>Writer()</strong> {
        }
        
        function write($message) {
        }
    }
?>
]]></php>
            The corresponding test changes are...
<php><![CDATA[
<?php
    require_once('../classes/log.php');
    require_once('../classes/clock.php');
    require_once('../classes/writer.php');
    Mock::generate('Clock');<strong>
    Mock::generate('Writer');</strong>

    class TestOfLogging extends UnitTestCase {
        function TestOfLogging() {
            $this->UnitTestCase('Log class test');
        }
        function testWriting() {
            $clock = &new MockClock($this);
            $clock->setReturnValue('now', 'Timestamp');
            $writer = &new <strong>MockWriter($this)</strong>;
            $writer->expectOnce('write', array('[Timestamp] Test line'));
            $log = &new Log($writer);
            $log->message('Test line', &$clock);
            $writer->tally();
        }
    }
?>
]]></php>
            In order to use the logging class we would need to code a file
            writer or other sort of writer, but at the moment we are only
            testing and so we do not yet need it.
            So, in other words, using mocks we can defer the creation of
            lower level objects until we feel like it.
            Not only can we design top down, but we can test top down too.
        </p>
        <p>
            <a class="target" name="bridge"><h2>Approaching the bridge</h2></a>
        </p>
        <p>
            Imagine for the moment that we had started the logging class
            from another direction.
            Pretend that we had coded just enough of the <code>Log</code>
            to realise we needed a <code>Writer</code> somehow.
            How would we have included it?
        </p>
        <p>
            Well, inheriting from the writer would not have allowed us to mock it
            from the testing point of view.
            From the design point of view we would have been restricted to
            just one writer without multiple inheritence.
        </p>
        <p>
            Creating the writer internally, rather than passing it in the constructor,
            by choosing a class name is possible, but we would have less control
            of the mock object set up.
            From the design point of view it would have been nearly impossible
            to pass parameters to the writer in all the different formats ever needed.
            You would have to have the writer restricted to say a hash or complicated
            string describing the target details.
            Needlessly complicated at best.
        </p>
        <p>
            Using a factory method to create the writer internally would be
            possible, but would mean subclassing it for testing so that the factory
            method could be replaced with one returning a mock.
            More work from the test point of view, although still possible.
            From the design point of view it would mean a new logger subclass
            for every type of writer that we want to use.
            This is called a parallel class hiearchy and obviously involves
            duplication.
            Yuk.
        </p>
        <p>
            At the other extreme, passing or creating the writer on every
            message would have been repetitive and reduced the
            <code>Log</code> class code to a single
            method, a sure sign that the whole class has become redundant.
        </p>
        <p>
            This tension between ease of testing and avoiding repetition
            has allowed us to find a flexible and clean design.
            Remember our brief yearning for multiple inheritence?
            We have replaced it with polymorphism (lots of writers) and separated the
            logging hierachy from the writing hierarchy.
            We connect the two through this simpler <code>Log</code>
            by aggregation.
            This trick is actually a design pattern called a &quot;Bridge&quot;.
        </p>
        <p>
            So we have been pushed by test code (we haven&apos;t written much else yet)
            into a design pattern.
            Think about this for a second.
            Tests improve code quality, certainly in my case, but this is
            something far more profound and far more powerful.
        </p>
        <p>
            Testing has improved the design.
        </p>
        <p>
            <a class="target" name="design"><h2>Mock down design</h2></a>
        </p>
        <p>
            Creating a mock object is as easy as creating the interface in text
            form.
            If you have UML or other tools that create these interfaces for you,
            then you have an even more flexible route to quickly generate
            test objects.
            Even if you do not, you can switch from white board drawing, to
            writing a test case, to writing a mock, to generating an interface
            which takes us back to the whiteboard drawing again, fairly easily.
            Like refactoring, design, code and test become unified.
        </p>
        <p>
            Because mock objects work top down they can be bought into the design
            more quickly than normal refactoring, which requires at least
            partially working code before it kicks in.
            This means that the testing code interacts with the design faster
            and this means that the design quality goes up sooner.
        </p>
        <p>
            A unit tester is a coding tool.
            A unit tester with mock objects is a design tool.
        </p>
    </content>
    <internal>
        <link>
            <a href="#mock">Mock now</a>, code later.
        </link>
        <link>
            We derive <a href="#bridge">the bridge pattern</a>.
        </link>
        <link>
            <a href="#design">Designing and testing</a> hand in hand.
        </link>
    </internal>
    <external>
        <link>
            This tutorial follows <a href="boundary_classes_tutorial.php">Boundary classes</a>.
        </link>
        <link>
            You will need the <a href="simple_test.php">SimpleTest testing framework</a>
            to try these examples.
        </link>
        <link>
            For more mock object discussion see the
            <a href="http://www.xpdeveloper.org/xpdwiki/Wiki.jsp?page=MockObjects">Extreme Tuesday Wiki</a>
            or the
            <a href="http://c2.com/cgi/wiki?MockObject">C2 Wiki</a>
        </link>
    </external>
    <meta>
        <keywords>
            software development,
            php programming tutorial,
            programming php test cases,
            software development tools,
            php tutorial,
            free php code,
            architecture,
            php examples,
            mock object examples,
            junit style testing,
            php testing frameworks,
            unit test,
            mock objects in PHP,
            php testing
        </keywords>
    </meta>
</page>