Not everyone is a fan of test-driven development.
Writing unit tests for your code can be a laborious task, and it has no immediate benefit to your customers. So why waste any time writing the tests, right?
This past week, a colleague of mine summed up the problem pretty well:
I’ve finished writing the code for that feature. Now I just need to take some time to sit down and write some unit tests for it.
He’s not following test-driven development. Instead, his tests come in as an afterthought. Actually, most of us code this way.
We write a chunk of code to perform a task. Then a customer asks for a new feature, so we write some more code. Then a new hire comes in to the company demanding we take some time to go back and document our existing code base with unit tests.
It’s not fun. It’s not glamorous. It feels like a waste of time.
But I still feel it’s the way things should be done.
The Advantage
If you write your unit tests first, it actually saves you time.
Now, instead of a long specifications document written in English and littered with biases and assumptions about functionality, you have a concise definition of what the code in your application must do. It doesn’t need to “flash,” “pop,” or “zing.” It just needs to pass the test.
If you just start writing code, I guarantee the client or your boss will come back halfway through the project and change the requirements on you. Even if they promise not to, they will.
Perceived needs change. Requirements change. If they do, write another test to include the new conditions – as a developer, your only concern should be writing code that passes the test.
Case Study
I wrote some code for a custom content management system. Since I didn’t want to be solely responsible for user management, I added some dynamic user creation capabilities. I wrote my tests, I wrote code that passed the tests, and I thought everything was peachy.
This is the basic function that creates a new user account on the server. It takes in an object that contains the username, their password, and their email address. It returns a JSON object containing all of the users in the database.
public JsonResult CreateUser(PostedUserModel post)
{
if (post == null) throw new ClientException("Invalid data.");
MembershipCreateStatus status;
var user = Membership.CreateUser(
post.UserName,
post.PlainTextPassword,
post.Email,
post.PasswordQuestion,
post.PasswordAnswer,
true,
out status);
// If the account wasn't created, we need to throw an
// exception to alert the user.
if (user == null)
{
// Default error message
string errorMessage = "Error: Unable to create user.";
switch (status)
{
case MembershipCreateStatus.DuplicateEmail:
errorMessage = "Email address is in use.";
break;
case MembershipCreateStatus.DuplicateUserName:
errorMessage = "Username is in use.";
break;
case MembershipCreateStatus.InvalidEmail:
errorMessage = "Email address is invalid.";
break;
case MembershipCreateStatus.InvalidPassword:
errorMessage = "Password is invalid.";
break;
}
throw new ClientException(errorMessage);
}
Roles.AddUserToRole(user.UserName, post.UserRole);
return this.Json(this.Users);
}
Then the QA team kicked the project back.
It turned out, you could create a user with all sorts of invalid characters in their name. My function allowed for [cci]test\user[/cci], [cci]test