Craftsmanship

Feature tests as interactive documentation

Feature tests are written to test the behavior of the software and not its implementation. It is written in business friendly, domain language as far as possible. Gherkin is a language used commonly to write feature tests with cucumber as the framework that executes the feature tests. I use gherkin/cucumber for writing feature tests and as far as this post is considered I will use gherkin tests and feature tests interchangeably.

The use of gherkin tests as executable documentation is a fairly known idea. What I realized recently was the not only are these tests executable documentation but they are also interactive documentation. So lets take an example of a feature test that tests the conversion of inventory quantity of a product into inventory level.

Feature: Convert inventory quantity for a product into inventory level

Given that the inventory quantity for a product with SKU “1234567890123” is “10”
And the inventory level is “in stock” when the quantity is between “5” and “3”
And the inventory level is “low” when the quantity is between “2” and “1”
And the inventory level is “out of stock” when the quantity is between “0” and “0”
When I convert the inventory quantity for SKU “1234567890123” into its inventory level
Then the inventory level for SKU “1234567890123” is “in stock”

Now this feature gives me a fairly good idea of what the business expects and how the feature should be implemented. If I make a change to the code for this feature, and I run my test and the test passes most likely I haven’t broken anything. Executable documentation FTW!

To take this a step further, what I can do with this feature test is, interact with it. Lets say I am wondering how the code handles boundary condition. I could very easily change the quantity in the Given from 10 to 3 and observe what happens.

Given that the inventory quantity for a product with SKU “1234567890123” is “3”
And the inventory level is “in stock” when the quantity is between “5” and “3”
And the inventory level is “low” when the quantity is between “2” and “1”
And the inventory level is “out of stock” when the quantity is between “0” and “0”
When I convert the inventory quantity for SKU “1234567890123” into its inventory level
Then the inventory level for SKU “1234567890123” is “in stock”

If I get the same answer as before which is in stock then it seems the code is working correctly for boundary condition as well, where as if it is not then we have a bug.

I hope this non-trivial example has been useful to understand what I mean by interactive documentation. I have found this “tweaking” of tests to be very useful to understand how the code works or should work.

For a long time, I thought that if the product owner/business analyst is not involved in writing the feature tests then these tests are an overhead. But of lately, I have been of the opinion that these tests are useful even for developers. If you are someone like me who jumps between different code bases frequently then these tests help come up to speed quickly with the business functionality without having to worry about the implementation/code. Also, as mentioned in the example above, I can interact with these tests to validate existing/new behavior quickly. The importance of writing these tests in a easy to understand, domain language cannot be overstated.

Standard
architecture, Craftsmanship, Design, Java, Refactoring

Soul coding

Yesterday I had a 12 hour non-stop[1] code fest to refactor a thin slice of 2-tiered web application into a 3-tiered one. It was very productive and I must say this is the kind of stuff that soothes my developer soul and hence the name. 🙂

The primary driver for the refactoring was that the core logic of the application was tightly coupled on both ends to the frameworks being used. On one side, it was tied to the web application framework, Play and on the other end the ORM, Ebean. We managed to move the business logic in to a separate tier, which is good on it own, but also let us unit test the business logic without bothering with the frameworks, which can be frankly quite nasty. As a follow on effect, we also managed to split the models into 2 variants, one to support the database via Ebean and the other to generate JSON views using Jackson. This let us separate the two concerns and test them nicely in isolation. Similarly, we could test the controllers in isolation. We got rid of bulk of our functional tests that were testing unhappy paths for which we had unit tests at the appropriate place viz., the controller, view model, service and database models.

I was quite amazed at how much we could get done in a day. Here are some of the important take aways from this experiment:

  • We had a discussion the previous day about how we wanted to restructure the code, primarily focusing on separation of responsbilities and improving unit testability of the code. We agreed upon certain set of principles, which served as a nice guideline going into the refactoring. On the day of refactoring, we realized that not all the things we discussed were feasible, but we made sensible adjustments along the way.
  • Keep your eye on the end goal. Keep reminding yourself of the big picture. Do not let minor refactorings distract you, write it down on a piece of paper so you dont forget.
  • Pairing really helps. If you are used to pairing, when you are doing refactoring at this scale, it doubly helps. Helps you keep focused on the end goal, solves problems quickly due to collective knowledge and also decision making cycle time is considerably reduced when making adjustments to the initial design. Also I would say pick a pair who is aligned with you on the ground rules of how you are going to approach development. You don’t want to get into a discussion of how or why you should be writing tests and what is a good commit size.
  • Having tools handy that get you going quickly. Between me and my pair, we pretty much knew what tool to use for all the problems at hand. At one point, we got stuck with testing static methods and constructors. My pair knew about PowerMock, we gave it a spin and it worked. And there it was, included in the project. Dont spend too much time debating, pick something that works and move on. If it does not work for certain scenarios, put it on your refactoring list.
  • Thankfully for us, we had a whole bunch of functional tests already at our disposal to validate the expected behavior, which was tremendously useful to make sure we weren’t breaking stuff. If you dont have this luxury, then pick a thin slice of functionality to refactor which you can manually test quickly.
  • Small, frequent commits. Again the virtuosity of this is amplified in this kind of scenario.
  • Say no to meetings. Yes, you can do without them for a day, even if you are the president of the company. 🙂

Have you done any soul coding lately? 🙂

[1] Ok, not quite 12 hours, but it was on my mind all the time. 😉

Standard
Craftsmanship

Pragmatism hurts too…

I spent quite a bit of my early career learning pragmatism. Frankly I did not know the word until I entered the industry. ;). I got this as a feedback, quite a few times, from my colleagues and more so from clients. They would say “You need to be more pragmatic”. What that really meant is, stop being so obsessed with you software engineering “craft” (my clients didn’t even think it was craft :)). Its ok to not have some part of the code tested before it hits production. Its ok to not have automated tests for some part of the code. Its ok to not pair program on critical code. Its ok to have 2000 lines of classes, with no tests, if it “already works”. Now I get it, right. All this does make sense, in a given context.

But, pragmatism hurts too. In this context, pragmatism means implicitly consenting to bad behavior. And again this might be ok, when you are in a bit of crunch, but when it starts becoming the norm, it begins to hurt, hurt a lot. When you see it becoming a norm, you need to start becoming a zealot for code quality. Best would be to have a good mix of zealots or for positive connotation, “craftsmen or craftswomen” on the team.

So, dont forget to promote craftsmanship just as you would promote pragmatism, especially if you are the leader of your software team.

Standard