Agile Javaв„ў: Crafting Code with Test-Driven Development

You have built your first test and corresponding class. You used StudentTest to help you build the Student class in an incremental manner. The test can also be used to ensure that any future changes you make don't break something you already did.

Unfortunately, the code isn't quite right. If you were to build more students, they would all respond to the getName message by saying they were Jane Doe. You'll mature both StudentTest and Student in this section to deal with the problem.

You can prove the theory that every Student object will answer the name Jane Doe by elaborating on the testCreate method. Add code to create a second student object:

public void testCreate() { Student student = new Student("Jane Doe"); String studentName = student.getName(); assertEquals("Jane Doe", studentName); Student secondStudent = new Student("Joe Blow"); String secondStudentName = secondStudent.getName(); assertEquals("Joe Blow", secondStudentName); }

The conceptual picture of the second student in memory is shown in Figure 1.8. Java finds space for the new Student object and stuffs it there. You won't know and shouldn't care where each object ends up. The important thing to get out of the memory diagram is that you now have references to two discrete objects in memory.

Figure 1.8. Objects in Memory

Start JUnit again. If you are running from the command line, start it as a background process (under Unix)[9] or use the start command (under Windows).[10] This will allow JUnit to run as a separate window, returning control to the command line. Whether you are using an IDE or running from the command line, you can keep the JUnit window open. Since JUnit reloads class files that are changed by compilation, you will not need to restart the JUnit GUI each time you make a change.[11]

[9] Append an & to the command, under most systems

[10] Prepend the command with the word start. For example:

start java -cp .;c:\junit3.8.1\junit.jar junit.awtui.TestRunner StudentTest

[11] For this to work, ensure that the checkbox labeled "Reload classes every run" is checked in the JUnit GUI.

The test fails:

junit.framework.ComparisonFailure: expected:<...oe Blow> but was:<...ane Doe>

Sure enough, since getName always returns "Jane Doe", the second assertEquals statement fails.

The problem is that you passed the student name to the constructor of the Student class, but the constructor does nothing with that name. Your code in the Student class is responsible for storing the name if anything is going to be able to refer to the name later.

You want the student's name to be an attribute of the studenta piece of information that is kept by the student as long as the student object is around. The most direct way to represent an attribute in Java is by defining it as a field, also known as an instance variable. You declare a field within the braces that delineate the start and end of a class. A field declaration can appear anywhere within the class as long as it appears outside of the methods defined in that class. But according to convention, you should place field declarations near either the start or the end of the class.

Like a local variable, a field has a type. Define the field myName to be of the type String within the Student class as shown here:

Class student { String myName; Student(String name) { } String getName() { return "Jane Doe"; } }

In the constructor body, assign the constructor's parameter name to myName:

Student(String name) { myName = name; }

Finally, return the field from the getName method (instead of the String literal "Jane Doe").

String getName() { return myName; }

JUnit should still be open, showing the comparison failure and red bar. Rerun the tests by pressing the Run button. The bar should turn green.

Take a look again at the test method:

public void testCreate() { Student student = new Student("Jane Doe"); String studentName = student.getName(); assertEquals("Jane Doe", studentName); Student secondStudent = new Student("Joe Blow"); String secondStudentName = secondStudent.getName(); assertEquals("Joe Blow", secondStudentName); }

The test demonstrates, and thus documents, how Student instances can be created with discrete names. To further bolster this assertion, add a line to the end of the test to ensure that you can still retrieve the proper student name from the first student object created:

public void testCreate() { Student student = new Student("Jane Doe"); String studentName = student.getName(); assertEquals("Jane Doe", studentName); Student secondStudent = new Student("Joe Blow"); String secondStudentName = secondStudent.getName(); assertEquals("Joe Blow", secondStudentName); assertEquals("Jane Doe", student.getName()); }

Note that instead of assigning the result of the message sendthe result of calling student.getName()to a local variable, you instead substituted it directly as the second parameter of assertEquals.

Rerun the test (after compiling). The success of the test shows that student and secondStudent refer to two distinct objects.

Категории