There are two kinds of constants:
1) Constants for Convenience/Readability
When writing code using TDD, each line of production code should exists because first there was a failing test that required that code to be written. And as you refactor the code, some magic values will get promoted into constants. Some of these might also be good as application settings, but for convenience (less code) they have been configured in code instead of an external configuration file.
In that case, the way that I write tests, both production and test code will use the same constants. The tests will specify that the constants are used as expected. But the tests won't repeat the value of a constant, such as in "assert MAX_ITEMS == 4
", because that would be duplicated code. Instead, the tests will check that some behaviour uses the constants correctly.
For example, here is an example application (written by me) which will print a Longcat of specified length. As you can see, the Longcat is defined as a series of constants:
public class Longcat {
// Source: http://encyclopediadramatica.com/Longcat
public static final String HEAD_LINES = "" +
" /\\___/\\ \n" +
" / \\ \n" +
" | # # | \n" +
" \\ @ | \n" +
" \\ _|_ / \n" +
" / \\______ \n" +
" / _______ ___ \\ \n" +
" |_____ \\ \\__/ \n" +
" | \\__/ \n";
public static final String BODY_LINE = "" +
" | | \n";
public static final String FEET_LINES = "" +
" / \\ \n" +
" / ____ \\ \n" +
" | / \\ | \n" +
" | | | | \n" +
" / | | \\ \n" +
" \\__/ \\__/ \n";
...
The tests verify that the constants are used correctly, but they do not duplicate the value of the constant. If I change the value of a constant, all the tests will automatically use the new value. (And whether the Longcat ASCII art looks right, it needs to be verified manually. Although you could even automate that with an acceptance test, which would be recommendable for a larger project.)
public void test__Longcat_with_body_size_2() {
Longcat longcat = factory.createLongcat(2);
assertEquals(Longcat.BODY_LINE + Longcat.BODY_LINE, longcat.getBody());
}
public void test__Fully_assembled_longcat() {
Longcat longcat = factory.createLongcat(2);
assertEquals(Longcat.HEAD_LINES + longcat.getBody() + Longcat.FEET_LINES, longcat.toString());
}
2) Universal Constants
The same application has also some constants which are never expected to be changed, such as the ratio between meters and feet. Those values can/should be hard coded into the test:
public void test__Identity_conversion() {
int feet1 = 10000;
int feet2 = FEET.from(feet1, FEET);
assertEquals(feet1, feet2);
}
public void test__Convert_feet_to_meters() {
int feet = 10000;
int meters = METERS.from(feet, FEET);
assertEquals(3048, meters);
}
public void test__Convert_meters_to_feet() {
int meters = 3048;
int feet = FEET.from(meters, METERS);
assertEquals(10000, feet);
}
And the production code looks like:
public enum LengthUnit {
METERS("m", 1.0), FEET("ft", 0.3048), PETRONAS("petronas", 451.9), LINES("lines", 0.009);
private final String name;
private final double lengthInMeters;
...
public int from(int length, LengthUnit unit) {
return (int) (length * unit.lengthInMeters / this.lengthInMeters);
}
}
Notice that I did not write any tests for the height of the Petronas Twin Towers, because that information is declarative (and declarative data rarely gets broken) and I had already written tests for the conversion logic (see above). If more similar constants are added (Eiffel Tower, Empire State Building etc.), they will be automatically located by the application and will work as expected.