We now have an idea of the application requirements as we defined our Use Case: Convert Temperature.
Using Test Driven Development we will implement this use case. We are also using UML to maintain our model.
Main project
Using Netbeans 6.0- Create a Java project (File | New Project... | Java | Java Application)
- Name the project something like TDD
- Select the desired location
- Uncheck Create Main Class
- Finish
UML Project
- Create a new UML project (File | New Project... | UML | Java Platform model)
- Name it something like TDD-UML
- Choose previous location. If the previous project folder was TDD select this project folder
- Finish
Create Class Diagram
- Create a Class Diagram and name it TDD Class Diagram
- Create a package tdd dragging a package from the palette
- Create a TemperatureConverter class
- Create a Nested Link from TemperatureConverter to tdd package
Generate code
- Select the tdd package
- Right click and select Generate Code...
- Select the previously created project, TDD as Target Project
- Select Source Packages as Source Root
- Press OK
Add testing infrastructure using XTest
- Select TDD project node in Projects
- Right click and select New | Other...
- Select Testing Tools and then XTest infrastructure (if it's not there go to the plugin manager and install it)
- Finish
Generate test cases
- Right click on the TemperatureConverter class node in the TDD project
- Select Tools | Create JUnit Test...
- IMPORTANT: Select JUnit 3.x
- Accept the name tdd.TemperatureConverterTest for the class
- Select Location: Unit Test Packages
- OK
Adding the test class to the UML model
- Right click on the editor window of the TemperatureConverterTest class
- Select Reverse Engineer...
- Select Use Existing UML Project and Target Project as TDD-UML
- OK
Complete the TDD Class Diagram
- Select TDD Class Diagram in the editor
- Drag and drop the TemperatureConverterTest class, which is inside the tdd package node, from the Model (TDD-UML | Model | tdd) to the diagram. Sometimes it's also needed to drag and drop the tdd package.
- You may want to change the color of the class to recognize it visually as a test, right click on it and select Class | Background color, we use green in this example.
Define the tests
Accordingly to our use case in TemperatureConverter we can define two tests- testCelsiusToFahrenheit, to test our converter
- testInvalidCelsiusTemperature, to validate that wrong temperatures are detected
An online converter can be found at http://www.onlineconversion.com/temperature.htm.
Let's add these components to get our tests finished.
Add the tests methods
In TDD Class Diagram- Right click on the TemperatureConverterTest Operations compartment and select Insert Operation... and insert the operations mentioned previously
- add testCelsiusToFahrenheit method
- add testInvalidCelsiusTemperature method
Add conversion table
In TDD Class Diagram- Right click on the TemperatureConverterTest Attributes compartment and add attribute conversionTable, set its type to Map<Integer, Integer> and be sure to check the static and final properties
- Set the default value to new HashMap<Integer, Integer>()
Generate the code
- Select the TemperatureConverterTest class only (be sure that no other component is selected), right click and Generate Code...
- Be sure to select Source Root: Unit Test packages
- OK
Initialization code
- Right click on the ConversionTable attribute and select Navigate to Source...
- If some imports are missing just press Shift+Ctrl+I
- Initialize the conversion table to this code, just after its definition
static {
// initialize (c, f) pairs
conversionTable.put(0, 32);
conversionTable.put(100, 212);
conversionTable.put(-1, 30);
conversionTable.put(-100, -148);
conversionTable.put(32, 90);
conversionTable.put(-40, -40);
conversionTable.put(-273, -459);
}
- Add this method (the prefix test is important for JUnit 3.x)
public void testCelsiusToFahrenheit () {
for (int c: conversionTable.keySet()) {
int f = conversionTable.get(c);
String msg = "" + c + "C -> " + f + "F";
assertEquals(msg, f, TemperatureConverter.celsiusToFahrenheit(c));
}
}
- Add this method (the prefix test is important for JUnit 3.x)
public void invalidCelsiusTemperature () {
try {
int f = TemperatureConverter.celsiusToFahrenheit(-274);
} catch (RuntimeException ex) {
if (ex.getMessage().contains("below absolute zero")) {
return;
}
else {
fail("Undetected temperature below absolute zero: " + ex.getMessage());
}
}
fail("Undetected temperature below absolute zero: no exception generated");}
- If some imports are missing just press Shift+Ctrl+I
Create the real code
We have now the tests for code that still doesn't exist. If everything was fine, you should see a light bulb in the line where TemperatureConverter.celsiusToFahrenheit is invoked and gives you the to Create Method celsiusToFahrenheit(int) in tdd.TemperatureConverterBut, sometimes things use to fail, and if it's not appearing use this work around:
- Double click on the TemperatureConverter class node, and go to the editor, add an empty line, delete it, just to force the change, then save the file.
- Is this a Netbeans bug ?
Running the tests
Our code compiles now, and we are in the condition of running the tests. After all, we are using Test Driven Development, don't forget it.Go to the TDD project node and
- Right click and select XTest | Run unit Tests...
We obtain something like this
We can see that one test failed because celsiusToFahrenheit converter was not implemented and the other because this method didn't generate the expected exception on an invalid temperature.
So, what's left is to go there an implement it !
Implement celsiusToFahrenheit
We need to replace the default implementation of this methodthrow new UnsupportedOperationException("Not yet implemented");
with
return (int)Math.round(celsius * 1.8 + 32);
Remember to change the parameter name from i to celsius.
Run the tests again
Undetected temperature below absolute zero: no exception generated
That's because we are expecting an exception to be thrown if the temperature is below the absolute zero but our first attempt doesn't do that.
Let's add this condition, but we first need the ABSOLUTE_ZERO_C constant.
- Synchronize the code with the UML by right clicking on the TemperatureConverter editor and then Reverse Engineeer...
- Select Use Existing UML PRoject and set it to TDD.
- Then OK and Yes to all.
Add ABSOLUTE_ZERO_C constant
Go to the TDD Class Diagram.
Right click on the TemperatureConverter Attributes compartment in the class diagram
- Add ABSOLUTE_ZERO_C, with type int, default value set to -273, and check static and final in properties
- Select the TemperatureConverter class and right clicking select Generate Code..., be sure to select Source Root: Source pckages
- Select Add mergers to existing source
- OK
Now
- Select the celsiusToFahrenheit method and then right clicking Navigate to Source...
Modify celsiusToFahrenheit
Add the absolute zero check
if (celsius < ABSOL?UTE_ZERO_C) {
throw new RuntimeException("Invalid temperature: " + celsius + " below absolute zero");
}
return (int)Math.round(celsius * 1.8 + 32);
For some reason (Netbeans bug ?) conversionTable disappear from the Class Diagram and from the model, but it's still there because it's not possible to add another attribute with the same name and if code is generated the attribute is still there.
No comments:
Post a Comment