Tests as Documentation


The test method testStudentStatus ensures that students report the appropriate full-time or part-time status. It also ensures that the Student class correctly adds credits.

What the test does not do is exhaustively test every possibility. The general strategy for testing is to test against 0, 1, many, and any boundary conditions and any exceptional cases. With respect to student credits, the test would ensure that a student with 0, 1, or 11 credits reported as part-time and that a student with 12 or 13 credits reported full-time. It would also test the results of unexpected operations, such as adding negative credits, or adding very high numbers of credits.

Test-driven development takes a slightly different approach. The strategy is similar, but the goals are not quite the same. The tests are not just a means of ensuring that the code is correct. In addition, test-driven design provides a technique for consistently paced development. You learn how to incrementally develop code, getting feedback every few seconds or minutes that you are progressing in a valid direction. Tests are about confidence.

Test-driven development also begins to affect the design of the system you are building. This is deliberate: Test-driven development teaches you how to build systems that can be easily tested. Far too few production systems have testability as a characteristic. With test-driven development, you will learn how to test classes in isolation from other classes in the system. This leads to a system where the objects are not tightly coupled to one another, the leading indicator of a well-design object-oriented system.

Finally, tests document the functionality that your classes provide. When you finish coding, you should be able to review your tests to understand what a class does and how it does it. First, you should be able to view the names of your tests to understand all the functionality that a given class supports. Second, each test should also be readable as documentation on how to use that functionality.

Code tests as comprehensive specifications that others can understand.


In the case of testStudentStatus, you as the developer have a high level of confidence that the production code for isFullTime is valid. It's only a single line, and you know exactly what that line of code states:

 return credits >= Student.CREDITS_REQUIRED_FOR_FULL_TIME 

You might consider the test sufficient and choose to move on, and in doing so you wouldn't be entirely out of line. Again, tests are largely about confidence. The less confident you are and the more complex the code, the more tests you should write.

What about unexpected operations? Won't it destroy the integrity of a Student object if someone sends a negative value for number of credits? Remember, you are the developer building this system. You are the one who will control access to the Student class. You have two choices: you can test for and guard against every possible exceptional condition, or you can use the design of your system to make some assumptions.

In the student information system, the CourseSession class will be the only place where the Student number of credits can be incremented; this is by design. If CourseSession is coded properly, then it will have stored a reasonable number of credits. Since it will have stored a reasonable number of credits, there is theoretically no way for a negative number to be passed to Student. You know that you have coded CourseSession properly because you did it using TDD!

Of course, somewhere some human will have to enter that number of credits for a course session into the system. It is at that pointat code that represents the user interface levelthat you must guard against all possibilities. A human might enter nothing, a letter, a negative number, or a $ character. The tests for ensuring that a reasonable positive integer is passed into the system will have to be made at this point.

Once you have this sort of barrier against invalid data, you can consider that the rest of the system is under your controltheoretically, of course. With this assumption, you can remove much of the need to guard the rest of your classes against bad data.

In reality, there are always "holes" that you unwittingly use to drop defects in your code. But a large key to success with test-driven development is to understand its heavy emphasis on feedback. A defect is an indication that your unit tests were not complete enough: You missed a test. Go back and write the missing test. Ensure that it fails, then fix it. Over time you will learn what is important to test and what is not. You will learn where you are investing too much time or too little time on the tests.

I have confidence in testStudentStatus and the corresponding implementation. Where the test is lacking is in its ability to act as documentation. Part of the problem is that you as a developer have too much knowledge about how you have coded the functionality. It helps to find another developer to read the test to see if it describes all of the business rules and boundaries properly. If another developer is unavailable, take a step back and try to look at the test as if you had never seen the underlying code. Does it tell you how to use the class? Does it demonstrate the different scenarios? Does it indicate the constraints or limitations of the code being testedperhaps by omission?

In this case, perhaps all that is needed is to let the test use the same constant that Student uses. The test becomes a good deal more expressive by virtue of doing so. The boundaries of full-time are now implicit and reasonably well understood.

 public void testStudentStatus() {    Student student = new Student("a");    assertEquals(0, student.getCredits());    assertFalse(student.isFullTime());    student.addCredits(3);    assertEquals(3, student.getCredits());    assertFalse(student.isFullTime());    student.addCredits(4);    assertEquals(7, student.getCredits());    assertFalse(student.isFullTime());    student.addCredits(5);    assertEquals(Student.CREDITS_REQUIRED_FOR_FULL_TIME,       student.getCredits());    assertTrue(student.isFullTime()); } 



Agile Java. Crafting Code with Test-Driven Development
Agile Javaв„ў: Crafting Code with Test-Driven Development
ISBN: 0131482394
EAN: 2147483647
Year: 2003
Pages: 391
Authors: Jeff Langr

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net