A lot happens from the time that a business owner envisions an idea in their mind and the time that the idea becomes software. The trick is getting through the whole software development process without losing the original idea.
Have you ever played that party game when you go around the circle whispering the same phrase and when you get to the end the phrase is completely different than when it started? Software development ends up like that a lot (especially when the business owner is changing the original idea!).
The problem is that we all speak a different languages. Developers speak one language, BAs another, PMs another, DBAs another, users another, business people another, executives another, and so on. So a large part of our job is learning how to translate what these people want into developer language (code), and doing it so that, in the end, the software speaks to them in their language.
Here’s a simple example:
Executive: “When we hire new employees, we need to make sure that they have a computer ready for them when they start.”
Business analyst: “User will enter the number of new employees on the screen. The system will check the inventory and make sure that we have enough machines, monitors, keyboards, and mice for the new employees. Each employee should have two monitors.”
Now it’s our turn. As developers, we have to do several translations in the process of writing the software. The DBA (or you) may have to design the database schema. You (the developer) have to write the code.
Remember, the goal is to not lose the original idea. That means that each time we “translate” the original idea, we need to do it little bits at a time.
Usually business analysts don’t give you specs that are written exactly how you want them. This is not a knock on BAs, but they speak a different language than us developers. What we really want from them is a set of acceptance criteria. In other words, we need to know what we have to do in order to complete the feature. Not only that, we need to know how we are going to test our feature so that we know that it’s working.
So let’s take the business analyst’s specs and translate them into acceptance criteria:
Given a stash of unused hardware
When a user enters the number of new employees
Then it should verify that we have one machine for each new employee
Then it should verify that we have two monitors for each new employee
Then it should verify that we have one keyboard for each new employee
Then it should verify that we have one mouse for each new employee
When we don’t have enough machines for new employees
Then we need to order new machines so that we have one for each new employee
When we don’t have enough monitors for new employees
Then we need to order new monitors so that we have two for each new employee
When we don’t have enough keyboards for new employees
Then we need to order new keyboards so that we have one for each new employee
When we don’t have enough mice for new employees
Then we need to order new mice so that we have one for each new employee
We’re not saying anything drastically different from what the business analyst said. But the way that we wrote it is important. Notice the use of the words given, when, then. Here’s the next translation:
public class Given_a_stash_of_unused_hardware
{
}
public class When_a_user_enters_the_number_of_new_employees
: Given_a_stash_of_unused_hardware
{
[Test]
public void Then_it_should_verify_that_we_have_one_machine_for_each_new_employee()
{
}
[Test]
public void Then_it_should_verify_that_we_have_two_monitors_for_each_new_employee()
{
}
[Test]
public void Then_it_should_verify_that_we_have_one_keyboard_for_each_new_employee()
{
}
[Test]
public void Then_it_should_verify_that_we_have_one_mouse_for_each_new_employee()
{
}
}
public class When_we_don't_have_enough_machines_for_new_employees
: Given_a_stash_of_unused_hardware
{
[Test]
public void Then_we_need_to_order_new_machines_so_that_we_have_one_for_each_new_employee()
{
}
}
public class When_we_don't_have_enough_monitors_for_new_employees
: Given_a_stash_of_unused_hardware
{
[Test]
public void Then_we_need_to_order_new_monitors_so_that_we_have_two_for_each_new_employee()
{
}
}
public class When_we_don't_have_enough_keyboards_for_new_employees
: Given_a_stash_of_unused_hardware
{
[Test]
public void Then_we_need_to_order_new_keyboards_so_that_we_have_one_for_each_new_employee()
{
}
}
public class When_we_don't_have_enough_mice_for_new_employees
: Given_a_stash_of_unused_hardware
{
[Test]
public void Then_we_need_to_order_new_mice_so_that_we_have_one_for_each_new_employee()
{
}
}
This is how you do behavior driven development. We took our acceptance criteria and wrote them out as code in the form of unit tests. These unit tests will prove that our code is working (when we get to that point) and it will also act as our documentation of what the code is supposed to do.
The reason that this is important is that we’re still in the middle of doing our translation. A lot of people take the tech specs and immediately start writing implementation code. But by doing that, you skip a step in the translation, and when you do that, you risk losing some of the original intent of the feature. This is one of the reason why writing tests before you write implementation code is important. First of all, if you write your tests first, then when they’re passing, you know that you’re done. Second, you’ve written out what the feature is supposed to do — nothing more, nothing less. This is going to help us implement the feature without losing the original intent of the person who thought it up.
Now, yes now, you can go write the implementation code. By focusing on what the end product should be and since you wrote your tests first, this part should be easy. It’s much easier to achieve your goal when you know what the goal is!
In case you haven’t heard, the Software Engineering 101 Conference is going on in Columbus on September 23. This is a one-day event where you will learn about software design topics and techniques such as object-oriented programming and the SOLID software design principles as well as a super-special hands-on test driven development session! This is all good stuff that every developer should know and it will be well worth your time. Rather than recount all the details, I’ll let you read more on Jim’s blog.
This is a FREE event, so all you have to do is ask your employer if you can go, they don’t even have to pay anything for it. I feel like I say this a lot, but these skills are essential for any software developer and have revolutionized the way that I write code.
Registration is limited, and I expect that it will fill up relatively soon, so don’t wait! You can click here to register.
Test driven development is a proven technique that will help you write well tested and well designed code, but it takes some practice. Here are some sample projects that you can work through to get some practice, along with my completed solutions so that you can see how I did it (not that my way is the only right way, of course). (Note: you can also pull the code doing using SVN from here: http://tdd-starter-kit.googlecode.com/svn/trunk/).
If you’re in the Columbus, OH area and you want me to come give my TDD talk (which these samples came from), send me an email and let me know and we’ll work something out.
Here are some links if you want to do some more reading:
Test Driven Development
TDD Design Starter Kit: It’s All about Assigning Responsibilities
TDD Design Starter Kit - State vs. Interaction Testing
TDD Design Starter Kit - Dependency Inversion Principle
TDD Design Starter Kit – Responsibilities, Cohesion, and Coupling
TDD Design Starter Kit - Static Methods and Singletons May Be Harmful
Succeed with TDD by designing with TDD
Unit Testing Business Logic without Tripping Over the Database
Haacked on TDD and Jeremy’s First Rule of TDD
Jeremy’s Second Law of TDD: Push, Don’t Pull
Achieve Better Results by following Jeremy’s Third Law of TDD: Test Small Before Testing Big
How much design before unit testing, and how much design knowledge before TDD?
So How do You Introduce TDD into an Organization or Team?
Pair Programming Bot
Why write unit tests?
Empirical Studies Show Test Driven Development Improves Quality
The Relative Cost of Fixing Defects
Rhino Mocks
StructureMap / dependency injection
Introduction to StructureMap
Jeremy Miller’s blog (author of StructureMap)
DimeCasts on StructureMap
Inversion of Control with the Plugin Pattern
What’s so great about Inversion of Control?
The Dependency Injection Pattern – What is it and why do I care?
Behavior Driven Development
Behavior Driven Development article from CODE Magazine - this one is a must read if you want to understand BDD.
BDD Wiki
Introducing BDD
Software Design
Writing Testable Code Is About Managing Complexity
Writing Testable Code
NBehave
NBehave.org
NBehave on CodePlex
NBehave source code
ReSharper
ReSharper website
ReSharper TDD Productivity Plugin
NUnit
Someone asked me the other day what new tool or technique they should learn, and the first thing that came to my mind was test-driven development. Test driven development is a proven technique that will help you write well tested and well designed code by writing your tests before you write your implementation code.
The best way to learn TDD is to watch someone do it. So on Thursday, July 23 from 12:00-1:00, I’ll be doing a lunch & learn presentation on TDD at the Quick Solutions office at Polaris (440 Polaris Pkwy, Suite 500 (5th floor), Westerville).
Lunch is provided (awesome!), so if you’re coming, please email amorey at quicksolutions dot com and let us know that you’ll be there.
I’ll show you the tools and techniques that I use to do test driven development and we’ll walk through the TDD process for some simple features. We’ll cover such tools and topics as NUnit, Rhino Mocks, behavior driven development with NBehave, and dependency injection, and I’ll give you the base code that I use on all projects to help write unit tests so that you can hit the ground running.
Can’t make it? I can come out to your place of work if you’re in the Columbus area and give this presentation again. If you’re interested, send me an email and we’ll set something up.
You can get information about the current user who is logged in using Thread.CurrentPrincipal. The problem with this is that it’s difficult to work with it in tests. It forces you to set the current principal in your test setup so that your code will work correctly, and this can be difficult if not impossible in situations where you’re using Windows Authentication (if you’re using Forms Authentication, it’s not quite as painful, but it’s still a pain).
I don’t like to actually have calls to Thread.CurrentPrincipal scattered throughout my code. It makes my code less readable. Usually if I’m checking the principal, I want to know what user is logged in. That leads to code like this:
var currentUser = null;
var principal = Thread.CurrentPrincipal as MyCustomPrincipal;
if (principal != null)
currentUser = ((MyCustomPrincipal) Thread.CurrentPrincipal).User;
Ick. I don’t want to write this over and over throughout my code. What if I had a class called PrincipalFactory that dealt with everything having to do with the principal? My class could look like this:
public class PrincipalFactory : IPrincipalFactory
{
public MyCustomPrincipal GetCurrent()
{
return Thread.CurrentPrincipal as MyCustomPrincipal;
}
public User GetCurrentUser()
{
var principal = GetCurrent();
return (principal != null ? principal.User : null);
}
public void SetCurrentUser(User user)
{
Thread.CurrentPrincipal = new MyCustomPrincipal(user);
}
}
Now when I want to know who the current user is, I can write…
var currentUser = _principalFactory.GetCurrentUser();
Isn’t that much easier on the eyes?
It’s much easier to test too. I always use dependency injection and StructureMap, which also makes testing easier. Let’s say I had a class like this:
public class AccountController : Controller
{
private IPrincipalFactory _principalFactory;
public AccountController(IPrincipalFactory principalFactory)
{
_principalFactory = principalFactory;
}
[Authorize]
public ActionResult Index()
{
var model = new IndexViewModel();
model.WelcomeMessage = "Hello, " + _principalFactory.GetCurrentUser().Username;
return View(model);
}
}
In my test code, I’m going to use Rhino Mocks to stub out the IPrincipalFactory. So the test will essentially pretend that a user named “Jon” is the current user and then we’ll make sure that the Index() method does the right thing.
using System;
using NUnit.Framework;
using Rhino.Mocks;
[TestFixture]
public class When_displaying_the_index_view()
{
[Test]
public void Should_display_the_welcome_message_for_the_current_user()
{
// Arrange
var principalFactory = MockRepository.GenerateStub<IPrincipalFactory>();
principalFactory.Stub(pf => pf.GetCurrentUser()).Return(new User { Username = "Jon" });
var controller = new AccountController(principalFactory);
// Act
controller.Index();
// Assert
Assert.IsTrue(((IndexViewModel)_controller.ViewData.Model).WelcomeMessage, Is.EqualTo("Welcome, Jon"));
}
}
This is much easier and cleaner than having to deal with Thread.CurrentPrincipal all over the place. If you didn’t hide Thread.CurrentPrincipal inside the PrincipalFactory class, you could still test it by setting Thread.CurrentPrincipal in the beginning of your test. I’ve been on projects that did it this way, and inevitably someone forgets to set Thread.CurrentPrincipal back to what it was before the test, or an exception happens and somehow the code that sets it back to the original value doesn’t run, and now tests will have different outcomes depending on what order you run them and which ones you run. This is usually a real pain to debug because you don’t know which test is causing the problem. Not fun.
With all of the talk about unit tests and test driven development, there is little talk about writing tests for stored procedures and other database code. I would argue that testing stored procs is just as important (if not more important) than testing .NET code because (a) you don’t recompile all of your SQL code when you change something and (b) bugs in stored procs can lead to really bad things like data corruption that are hard to fix.
Writing tests for database code is not rocket science. There are no SQL testing frameworks to learn (at least I don’t know of any), but that’s fine because you can do just fine with plain ol’ T-SQL.
Let’s say I have a database that looks like this:
create table Users
(
UserId int not null primary key identity(1,1),
Username varchar(255)
)
create table Roles
(
RoleId int not null primary key identity(1,1),
RoleName varchar(255)
)
create table UsersInRoles
(
UserId int not null, -- foreign key to Users
RoleId int not null, -- foreign key to Roles
)
And let’s say that we were going to write a stored proc that looks like this:
create procedure [dbo].[IsUserInRole]
(
@UserId int,
@RoleId int
)
as
begin
if exists (select 1 from UsersInRoles where UserId = @UserId and RoleId = @RoleId)
return 1
return 0
end
We can easily write a test for this. The test might look like this:
create procedure [dbo].[unittest_IsUserInRole]
as
begin
begin transaction
-- set stuff up
declare @UserId int
declare @AnotherUserId int
declare @RoleId int
declare @AnotherRoleId int
insert into Users (Username) values ('test')
set @UserId = scope_identity()
insert into Users (Username) values ('another user')
set @AnotherUserId = scope_identity()
insert into Roles (RoleName) values ('test role')
set @RoleId = scope_identity()
insert into Roles (RoleName) values ('another test role')
set @AnotherRoleId = scope_identity()
insert into UsersInRoles (UserId, RoleId) values (@UserId, @RoleId)
-- test stuff
declare @result int
-- should return true if the user is in the role
exec @result = IsUserInRole @UserId, @RoleId
if @result <> 1
raiserror('Test failed: should return true if the user is in the role', 16, 1)
-- should return false if user is not in the role but is in another role
exec @result = IsUserInRole @UserId, @AnotherRoleId
if @result <> 0
raiserror('Test failed: should return false if user is not in the role but is in another role', 16, 1)
-- should return false if user is not in the role but another user is in the role
exec @result = IsUserInRole @AnotherUserId, @RoleId
if @result <> 0
raiserror('Test failed: should return false if user is not in the role but another user is in the role', 16, 1)
-- should return false if the user does not exist
declare @NonExistantUserId
select @NonExistantUserId = max(UserId) + 1 from Users
exec @result = IsUserInRole @NonExistantUserId , @RoleId
if @result <> 0
raiserror('Test failed: should return false if the user does not exist', 16, 1)
-- should return false if the role does not exist
declare @NonExistantRoleId
select @NonExistantRoleId = max(RoleId) + 1 from Roles
exec @result = IsUserInRole @UserId, @NonExistantRoleId
if @result <> 0
raiserror('Test failed: should return false if the role does not exist', 16, 1)
rollback transaction
end
Notice that I’m beginning a transaction at the beginning of the test procedure and I’m rolling it all back at the end. That way all of the test objects that I insert into the tables won’t be left around after the test is done. If one of my assertions fails, the test will throw an error (but everything will still get rolled back).
The last step is to call the test proc from a standard .NET unit test using your favorite testing framework, which might look like this:
[TestFixture]
public class When_checking_to_see_if_a_user_is_in_a_role
{
[Test]
public void Run_IsUserInRole_tests()
{
// call the test stored procedure here
}
}
That’s it! Now you can have peace of mind when you write T-SQL code too!
As if I needed any more convincing, I ran into another situation where TDD saved me from a lot of pain.
On my current project, we have a lot of client-side Ajax-y goodness. The feature that I was working on was dragging something from one part of the page and dropping it on another part of the page. I was using jQuery UI’s Draggables and Droppables to do this. I got most of the way done, and then realized that jQuery UI draggable/droppable support does not work completely in IE because of some IE bugs that they apparently have not worked around yet. (A newer version of the jQuery UI has come out since I wrote this so it’s probably fixed now.)
Luckily, I wrote all my JavaScript the TDD way, so all of the UI concerns were separated out from the code that has the logic in it. So I was easily able to pull out jQuery’s drag and drop and replaced it with Yahoo UI’s drag and drop in only 4 hours (and I’ve never used YUI before). The only code I needed to change was the JavaScript that was in the ASPX page (this is all of the untestable stuff that wires stuff up to actual DOM objects). All of the underpinning logic (some of which still uses jQuery, as does the rest of the site) all works the same as it did before.
If TDD hadn’t led me into well-designed, object-oriented JavaScript, doing this would have taken a lot longer than 4 hours. In fact, it probably would’ve been really difficult to do without just rewriting most of what I had done. But this is one of the reasons that we write tests first — it gives a code based is easy to change and easy to maintain. I experienced that first-hand today.
The next time you are getting lazy about not writing your tests first and you need someone to keep you in line, you have someone that you can turn to!
On my current project we will be using QUnit to writing tests for our JavaScript. What would be really nice is if our CI build could run JavaScript unit tests.
Well, it can! It only takes a few magic potions and sacrificing a few small animals.
Luckily someone had done this before and blogged about it so I pretty much just had to follow instructions. Here’s the long and short of it:
- We had to get WatiN, which will be used to create an instance of Internet Explorer that will run our HTML page with the QUnit tests in it.
- We had to get the IterativeTest addin for NUnit. This will create one NUnit test for each QUnit test.
In the code, I added an IterativeTest (a test decorated with the [IterativeTest] attribute) that will run all of the QUnit tests. Since we all use ReSharper and ReSharper doesn’t recognize the [IterativeTest] attribute, I also added a normal NUnit [Test] that will run all of the QUnit tests inside one test and fail if any of the QUnit tests fail. So we should have all of our bases covered.
Here are the slides and code from the Unit Testing JavaScript talk. Or if you one of those people you can get it all from the Subversion repository at http://minisudoku.googlecode.com/svn/trunk/.
(UPDATE 2/25/09: these links point to the latest versions of the slides and the code, which now include examples using both JSSpec and QUnit.)
« Newer Posts — Older Posts »