Monday, July 8, 2019

Using Unit Tests for Communication

Expressing software requirements is hard. Software is abstract and intangible, you can't touch it see it or feel it, so talking about it can become very difficult.

There are plenty of tools and techniques that people use to make communicating about software easier like UML diagrams or software state diagrams. Today I'd like to talk about a software development technique my team has been using for communication: Test Driven Development.

Test Driven Development (TDD) is a very involved software development approach, and I won't go into it in depth in this post, but here's a quick summary

  1. Write tests before code, expecting them to fail
  2. Build out code that adds the behavior the test looks for
  3. Rerun the test
On my team we've started trying to use unit tests as a communication tool. Here's an example.

At my company we use the concept of a unique customer ID that consists of the first two digits of the state the customer's headquarters is in, and a sequential 3 digit number. At least, most of the time. The Site ID concept grew organically, and like any standard that starts organically it has exceptions. Here are a few that I'm aware

  1. One very large customer uses an abbreviation of their company name, instead of state code
  2. Most systems pad with zeros, some do not (e.g. TX001 in some systems is TX1 in others)
  3. Most systems use a 3 digit number, some use four (e.g. TX001 vs TX0001)
After we added "site id converters" to a few modules in our config management code we decided it was time to centralize that functionality and build out a single "site id converter" function. When I was writing the card it was clear there was enough variation that it was going to take a fair amount of verbiage to spell out what I wanted. Let's give it a try for fun.

Please build a function that takes a site id (the siteid can be two digit state code and 1, 3, or 4 digits, or 3 digit customer code and 1, 3, or 4 digits). The function should also take a "target domain" which is where the site id will be used (for example "citrix" or "bi"). The function should convert the site id into the right format for the target domain. For example if TX0001 and Citrix is passed in the function should convert to "TX001". If TX001 and "BI" are passed in the function should convert to "TX0001".

It's not terrible, but it gets more complex when you start unpacking it and notice the details I may have forgotten to add. What if the function gets passed an invalid domain? What should the signature look like? What module should the function go into?

And it gets clarified when we add a simple unit tests with lots of details that feel a little awkward to put in the card.

Describe "Get SiteID by domain tests" {
    InModuleScope CommonFunctions {
        It "converts long id to short for citrix" {
            Get-SiteIdByDomain -siteid "TX0001" -domain "citrix" | Should be "TX001"

I'm not suggesting you stop writing cards or user stories, rather the right answer usually seems to be a little of both. Some verbiage to tell that background, give some motivation for the card, and open a dialog with the engineer doing the work. Some unit tests to give more explicit requirements on the interface to the function.

This approach also leaves the engineer free to pick their own implementation details. They could use a few nested "if" statements, they could use powershell script blocks, or anything else they can think of (that will pass a peer review). As long as it meets the requirements of the unit test the specifics are wide open.

A note about TDD

Please note, I'm not a TDD fanatic. TDD is a really useful tool that drives you to write testable code that keeps units small, makes your functions reasonable, and lets you refactor with confidence. As long as your code still passes your unit test sweet you can make any changes you want.

But it's not a magic hammer. In my experience it's not a good fit to start with TDD when

  • You're using an unfamiliar SDK or framework, TDD will slow down your exploration
  • You have a small script that is truly one time use, TDD isn't worth the overhead
  • You have a team that is unfamiliar with TDD, introducing it can be good, using it as a hard and fast rule will demoralize and delay

No comments:

Post a Comment