One of the Principles of the Agile Manifesto is the idea of Continuous attention to technical excellence
and good design
. As quoted in your textbook on page 180:
Testing does enhance quality, but it is really more of a discipline for helping developers work safely on code in[emphasis mine]brain-sizedchunks. It's more about problem decomposition and managing cognitive load than about quality.
On the other hand, the tests abide after the programming is done, and all of those unit tests [microtests] we write while doing TDD are there to help us know if the code we are currently working on breaks any existing code.[emphasis mine][Tim Ottinger, 2013, in Ashmore, p. 180]
What we need is an organizational culture that is focused on quality. This requires much more than simply
writing a plan and tracking to it, which is the normal way of doing things under Waterfall. Agile takes a
much more proactive approach to quality than Waterfall [which is 'reactive']. Under Waterfall, each part
of the application may have been tested at the lower level to show it works, but there may be no sense of
how well that code will work when it's integrated to make the full product. Agile, by constrast, normally
uses daily builds that integrate all of the currently working code into the application and runs tests on
the entire build. That way, if the build breaks, the team gets immediate feedback about what is broken
[and often where it is broken], so that the situation can be addressed and resolved right away.
This is advantageous because it is likely that the code is still fresh in the developer's mind. Further,
there will be a lot less code that has to be interpreted or waded through
to locate the defect.
Pair ProgrammingWe've actually seen this before, but it's worth re-visiting. Introduced by the XP method, it has two people shoulder-to-shoulder at the same computer working together, and trading off every 15-20 minutes. One is the typist, called the driver, and the other is the observer [in name only]. There are several benefits to this approach in terms of quality:
|
|
Also commonly called by its acronym TDD
, Test-Driven Development started with XP and has really become
kind of a de-facto standard for the way developers build and test their code. In Waterfall, the usual way
was to develop the code, then write the tests and run them later, near the end of the project life-cycle.
With Agile, however, the situation is pretty much reversed; the developer writes the tests first, then does
the code for the test, making it work properly with that test before moving on.
The idea includes writing ONE TEST AT A TIME, and only focusing on the code THAT WILL MAKE THAT TEST PASS.
Only one test is focused on and the developer will not move on until that test passes. One of the good
things this process fosters is that the developers must think about the requirements of the thing they are
working on, so that they can effectively test it in the way it will be used. That way they can think not
only about the requirements
, but also about the context
in which that method or function or
class will be in when it is part of the entire product.
Doesn't it take extra time to do this? Isn't it less efficient to have two people working at the same time on the same computer? Actually, the answer is NO to both. It may take a bit more time to write the test first for EVERY module or method or function, but it actually saves time over the course of the project because the test suite is more complete and is being built up as the product is being developed so that it is ready and complete [or nearly so] by the time the application is complete. In response to the second question, it is actually MORE efficient, because with both people focused on the same code, you get better error checking and defect identification than with one person alone, so you actually end up with shorter development time.
So, how does one go about this? There are five basic steps:
|
Be careful not to confuse this activity with debugging the code. Refactoring is NOT debugging. The code has already been debugged, you are just improving it in a way that is benign to the operation of the overall product. One of the nice things about TDD is that since you have a complete set of tests for everything, you can test each refactoring change and MAKE SURE you haven't broken anything. You can immediately find out if your change has caused damage, and can return to the previous version and find another way. Most of the time with refactoring this will not occur, and it will not be a problem. |
The idea of refactoring originally came from mathematics, since you are reducing the code in the same way you might reduce an equation, until no additional changes can be made.
It is possible, however, that sometimes refactoring can introduce bugs. Because of the way it is defined,
you may make changes to the internal structure of the code, as long as the way the code fits and operates
in the outer context
is unchanged. This might mean implimenting a completely different algorithm
internally for a method, with the stipulation that the outer calling signature, the allowed inputs, and
the expected outputs don't change.
For example, consider a method that implements a way of calculating the Another consideration that refactoring brings to light is the idea of |
|
Your textbook has a very complete example of Test Driven Development and Refactoring that begins on page 183
and continues through page 197. In these 15 pages the text walks you through creating and testing a game of
BlackJack for a casino customer. The rules of the game are given first, so that those who are not familiar
with the game can understand what is supposed to happen. Then the initial setup of the environment is
discussed in a set of steps that puts the development team in a place that is ready to write code. This
process is based on the use of
, which is a much-used testing framework for the
Java programming language.JUnit
In the subsequent pages, the text walks through nine different scenarios, giving highlights of the process at each step:
testDealFirstTwoCardsToPlayerSecondTwoCardsToDealer because that is what the test
will verify. Notice that this is a VERY descriptive name and in spite of its length will let
the reader of the code know EXACTLY what is being tested. This is actually a good coding practice,
not only for Java, but for other languages as well. Next, several method calls are set up, to test
the functionality of the class that will be tested. The tests should be run so that the developers
can see them fail. Finally the code that will be tested is written, calling the dealCard()
method twice to deal the first two cards to the player, then twice more for the dealer.
playHand(). All three possible scenarios for the cases are tested.
bust.
stand.
stand.
In the best case, there would be NO defects at all, but of course, that is not very realistic unless the code is relatively simple and is crafted with extreme care and attention to detail. Barring that, there WILL be defects that must be dealt with, even in the most thoughtfully developed code.
It is important to fix defects right away, even to the point of prioritizing them higher than writing new code. This makes sense, since writing new code that is based on buggy code only makes the problem worse as the team applies "band-aids" to new code to make it work with older code, possibly even having to modify the product design in the process. Further, it is usually easier to find and fix problems while the code is fresh in the developers' minds rather than waiting, so it's more efficient to do it right away. Finally, in a team environment, waiting makes it even more likely that a different person on the team has modified that code and is unaware of the problems that are being built upon; this situation increases the likelihood of a defect making it into released software, with the associated loss of reputation which can cost in customer satisfaction. For these reasons, it is important that proper testing be done at ALL levels, and that any defects which may be uncovered are dealt with immediately, or certainly within the next sprint. |
|
Usually, most defects are found very early, during the coding process itself. It is natural for developers
to test their code, and even more likely if they are using TDD. However, these are developmental bugs that
are fairly isolated to the particular modules. To help deal with this situation, Agile focuses on what is
called continual integration builds
in which the code is tested as soon as it is
committed to the repository as part of the overall build testing. THIS DOES NOT MEAN
THAT UPDATES ARE PART OF LIVE
PRODUCTS. That would be stupid. However, it does mean that an
integrated build is available to the team for testing at all times,
Customer feedback should also be part of some [not all] of the testing process. Alpha
tests are used to allow the customers an early view, so they can try things out. This activity is often
followed by Beta
tests which incorporate feedback from Alpha and let the customers perform
even more thorough testing, often using the product in its intended environment. Finally, there is
Usability
testing which involves the team watching the customer use the product to see
where there may be opportunities to improve interaction with it.
There are many different types
of testing but they fall into two main categories.
pair testingin which two people work together, one performing the test and one checking off the results. There is potential for error or for missing something, but there are some instances for which this may be the best, or even the ONLY, method that will work.
For this in-class exercise, I'm going to walk you through some testing philosophies using the Java
language. I'll be doing what's called live coding
for which you are free to follow along as I
code. To do this you will need an editor and the Java JDK installed on your computer; many of you may
already have this this set up on your computers. If not, you can download the java installer from
this location for
both mac and PC. If you want to take some time to do that, it's fine; it only takes a few minutes and
you can probably get it going over the mid-class break.
The link goes to the Oracle site for Java 15. This demonstration WILL NOT be using the JUnit 5 testing framework simply because so much has been added to it that it would take an entire class to get it working properly for everyone here. There is STILL a lot to JUnit 4, but it is easier to get working so we'll use that. Here are some links that you can use to get things set up and running:
The itemized activities I'll demonstrate are as follows:
test harness.
.javaclass file and the test harness to clean it up.