production constants in tests

When is it acceptable to share constants between the production code and the test code you are writing when practicing test first development?

I hadn't really thought about this until I came across a piece of code I was writing that used constants shared between test code and production code - as I was writing them something didn't sit right and I was getting the familiar code smell feeling.
Let me present the scenario first - I have a command builder that will return a SqlCommand and my test first process is evaluating the state of the SqlCommand object that gets returned. My context specification code is illustrated below, although the names have been made generic::

public class when_building_a_command_object_for_writing_data 
               : observations_for_a_sut_without_a_contract<DataCommandBuilder> 
{ 
     protected static SqlCommand command; 
     private static Data data; 
private context c = () => data = new Data() {Contact = "contact1";};
private because b = () => command = sut.GetCommandWithParametersDerivedFrom(data); [Observation] public void should_have_contact_parameter_with_correct_value() { command.Parameters[CustomParameters.Contact].Value .should_be_equal_to(data.Contact); } [Observation] public void should_have_contact_parameter_of_correct_size() { command.Parameters[CustomParameters.Contact].Size .should_be_equal_to(CustomParameterSizeFor.Contact); } } public static class CustomParameters { public const string Contact = "DataContact"; (...) } public static class CustomParameterSizeFor { public static int Contact = 25; }

Part of the actual implementation code is as follows:

     command.Parameters.Add(new SqlParameter() 
                         { ParameterName = CustomParameters.Contact,
                           SqlDbType = SqlDbType.VarChar, 
                           Size = CustomParameterSizeFor.Contact, 
                           Value = data.Contact });

This snippet of code above is the result of test driven process using the specification illustrated above it amongst other specs.
Can you spot the problem?

What was of immediate concern to me was the way the Size specification shared the same constant as the production code Size assignment does. At the moment the value is specified as 25 and the tests pass so the code appears great at first glance.

Let's suppose a scenario though where sometime later a different developer wants to change the size of the AdditionalContacts parameter to 10 (which isn't shown and is just hypothetical) but due to distractions she actually modifies the contact constant by mistake:

public static class CustomParameterSizeFor 
{
     public static int Contact = 10; 
}

What happens now is that the production code for the contact parameter size changes at runtime, but because the specification points at the constant it is changed too and the tests still pass.
Because the two are so intrinsically linked the test will never fail.

But what of the other constant sitting in the code ("CustomParameters.Contact") that represents the parameter name? Is this incorrect also?
I don't think it is - it is an identifier that enables the test to work with the same parameter name as the production code does and this is perfectly acceptable... we are not asserting anything on this value just using it.

So my conclusion is that shared constants are fine for identifiers but not for assertion values - pretty obvious when you step back and think about it.



0 comments: