-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathd10-tdd.tex
184 lines (149 loc) · 6.56 KB
/
d10-tdd.tex
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
%
%
% TODO: Add some notes on packages here
%
%
% TODO: do some slides or maybe add to notes with
%
% - how TDD separates WHAT from HOW
%
% - the TDD cycle:
% 1) write interface
% 2) write tests
% 3) write minimum code to compile all tests
% 4) write minimum code to pass all tests
% 5) REPEAT
%
\section{More on testing}
\label{sec:more-testing}
%
% TODO: add some notes on what to test and what not to test, look at
% notes from Uncle Bob.
%
\subsection{Additional annotations}
\label{sec:addit-annot}
In the former section we have seen how basic testing is performed. A
testing method is marked with the annotation \verb+@Test+, and this
indicates to JUnit that the method will be run as a test. The testing
method contains one (sometimes more) \emph{assertions}, which is
nothing more than a call to one of the methods of class
\verb+org.junit.Assert+, like \verb+assertTrue(...)+
and \verb+assertEquals(...,...)+.
In this section we are going to learn how to create slightly more
complicated tests.
\subsubsection*{Constraints: timeout and expected}
\label{sec:timeout}
Sometimes a bug does not result in an incorrect return value,
sometimes it results in an infinite loop or an extremely long response
time. If we want to specify a maximum time for our testing methods to
run, we can do it by passing a ``timeout'' parameter to annotation
\verb+@Test+, as in the following example:
\begin{verbatim}
@Test(timeout = 1000)
public void testsThatFinishedBeforeOneSecond() {
// ...
}
\end{verbatim}
If the method does not return a value before 1000 milliseconds have
passed, the test will fail.
We can also expect a method to return an exception, as in the
following example:
\begin{verbatim}
@Test(expected = IndexOutOfBoundsException.class)
public void testsNegativeIndecesFail() {
// ...
}
\end{verbatim}
If the method does not throw an \verb+IndexOutOfBoundsException+ in
this test, the test will fail. Note that this is quite the contrary of
the usual behaviour: when a testing method throws an exception, it is
usually a symptom of something not working as expected; but this not
always the case. Situations in which we may want to expect an exception
include requests to lists beyond their size, parsing strings that
are not in the right format, and passing negative parameters to
methods that only accept positive integers.
\subsubsection*{Initialisation: Before and After}
\label{sec:init-before-after}
Testing methods usually require the creation of some object. It is
quite common that this is needed for all testing method in a testing
class.
Instead of repeating the same code in each and every method, which
is boring and error-prone, we can create a method to do that before
every testing method is called. This method must be annotated with
\verb+@Before+. In the same way, if some cleanup must be performed
after each testing method ends and before the next testing method
starts, this should be marked with the \verb+@After+ annotation. See
the example:
\begin{verbatim}
@Before
public void buildUp() {
// A file is created here to be used in every test.
}
@After
public void cleanUp() {
// The file is deleted here, after each test ends
}
\end{verbatim}
Assuming that your testing class contains three testing methods, the
execution path would be: buildUp, first test, cleanUp, buildUp, second
test, cleanUp, buildUp, third test, cleanUp. Note that you can change
the names of the methods (buildUp and cleanUp are common choices).
\subsubsection{Heavy initialisation: BeforeClass and AfterClass}
\label{sec:heavy-init-befor}
Using \verb+@Before+ and \verb+After+ is appropriate in those cases in
which initialisation and clean-up are fast. However, if the resources
needed are costly to allocate and release, and if they are not changed
inside each testing method, then it is better to just do some
initialisation at the beginning of all tests and some clean-up at the
end of all test.
This is done by using the annotations \verb+@BeforeClass+ and
\verb+@AfterClass+. Examples of resources that are typically acquired and
released once per class include network connections and database
connections.
\subsection{Test-Driven Development}
\label{sec:test-driv-devel}
Test-Driven Development (TDD) is a programming methodology that advocates
that tests should be driven before the actual program. This is
radically different from writing the program first and then writing
the tests as we have done in the past.
The TDD methodology consists of three steps that are repeated in a
loop:
\begin{enumerate}
\item Write the tests for the next functionality/feature of the program. Make
sure they fail. If they do not fail, that means they are not testing
anything that was not tested before (and are redundant) or they are
incorrect (and should be fixed).
\item Write the minimal code that passes all the new tests.
\item Refactor the code to make it clearer and simpler. Run the tests
at the end to make sure the final functionality is right.
\end{enumerate}
There are four main benefits for this style of programming:
\begin{itemize}
\item As the tests are written in advance, the \emph{production} code
is written in a way that is easy to test.
\item As the tests are written in advance, all the code is tested by
at least one testing method. Otherwise, programmers can forget to
test some methods.
\item Writing the tests first makes the programmers think about the
real specification of the class or method, focusing on \emph{what}
needs to be done before their short-term memory is filled with
\emph{how} to do it.
\item Errors are detected early, when they are cheap to fix. It is
more difficult for bug to remain undetected until later in the
development, when it can be more costly to fix them.
\end{itemize}
The mains shortcoming is that it is difficult to see any progress at
the beginning of the project. Time is spent writing tests and nothing
``tangible'' can be shown to managers or clients. But the effort pays
in the long run, when the code evolves in a controlled way, with a
strong battery of tests that ensures that bugs do not re-appear and
that the functionality is always moving forward.
The TDD methodology can be combined with the ``find bugs once'' strategy
we already know. A program can be developed using TDD, finding most
bugs in the process, but some bugs may appear later in the development
cycle; if that happens, the ``find bugs once'' strategy results in
adding new tests that will find them as soon as they reappear.
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "main"
%%% End: