Monday, October 27, 2014

On Coaching Agile: Killer Questions - What do you want to test?

When coaching teams or individuals in agile techniques like TDD, Refactoring, Clean Code, CI, you name it then I've found out that certain questions yield interesting effects. My absolute favorite would be this one here:

What do you want to test?

In the meaning of: What feature or behavior do you want to test?

One of the key take aways in trainings on agile techniques would be the knowledge about and the appliance of the testing pyramid. Many teams I trained would have a test structure that imitates an upside down pyramid or a trapeze standing on the short edge or even a big letter T. When they attend a training they would learn about TDD and unit tests and are eager to write them for their code too. And what seems to be rather easy in the training with a well designed and small sample application tends to become overwhelmingly difficult when they get back to their own code base which usually would be more complex and more ridden with legacy code issues.

Nevertheless they start out and want to write the first unit test themselves. And here is where the trouble starts. How would you do that? Most of them lack experience of what a good unit test would look like. What unit should you begin with? And which tests would make sense? If you never ever tried to write unit tests for your production code these questions are tough. Everything is so much more complicated compared to end-to-end testing where there is no need to understand the inner structure of your code and where design issues are not preventing you from testing.

The following is condensed from several sessions with developers trying to put a piece of their code under test. There is no single developer who behaved exactly this way. I took the freedom to exaggerate the situation for the educational purpose. But please be aware that any part of this is taken from real situations: 

So, when I pair (actually: triple) with my developers before writing one single line of code I ask them: 

What do you want to test?

Most of the developers really have no good answer to this question. They are able to explain the implementation to its tiniest detail but they fail answering this rather simple question. That is why asking it will usually be the begin of a long discussion where they tell me what actually happens in their code which things get to be done and how. This is when I would guide the discussion to the structure of the code and the building blocks it consists of. We try to identify the parts that would not heavily make use of components which are not under control of the team. To begin with we would focus on the components that depend on components under control by the team to make things a little bit easier. Most of the time this initial discussion already brings about a better understanding of their own code and architecture which will help a lot in the upcoming part of the session. Besides, me as a coach I'm able to understand the coding on a relaively high level of abstraction which helps me to not get lost in too much details. I would gain enough knowledge to understand the features and what tests there should be. With that I'm able to guide them further down the road.

Once the component to put under test has been identified I would ask the question again: 

What do you want to test?

Many times this is when I get my tour through the component and what it would be doing one more time. This is when I try to find a nice unit to start testing with. Sometimes they would pick a unit to test themselves and sometimes not. Whatever they would pick I'd try to start with. It is important that they learn to decide themselves. It is important that they take over the lead as much as possible. It is not my show. It is not about how I would proceed. It's more about being a facilitator, to lend a helping hand. When there is a unit to test I ask one more time

What do you want to test?

Now it's about to find the method to test. Often the units picked had one method that would stand for what the whole unit is all about. Anything else in this unit are just private helper methods. Now I would try to nail them down on a certain behavior of this unit, a certain method to test. Usually I would get the same tour through algorithms and underlying layers again. And here it is important to come down to what a unit test is all about: testing a certain behavior for a defined input producing an expected output. And this is what they really have to understand here:

It is about this behavior. That's the reason we write unit tests. To secure a certain behavior of the unit.

It is not about testing all paths and all combinations. It is about testing a behavior which is directly derived from the acceptance criteria of a user story. Any code that is not covered by such acceptance criteria is useless for it implements stuff no one ever asked for. When developers write their tests with the implementation in mind then they might get a high code coverage but what we would really need is a high feature coverage. By asking the question

What (feature/behavior) do you want to test?

one would always stay in the outside-in perspective which helps to keep the acceptance criteria in mind. With that writing down expected results of tests becomes pretty easy for everything follows directly from the acceptance criteria.

So, to get to the point which tests have to be written I very frequently ask my question. It proofed to be of use to not let them go the whole tour again although they always try. Early interruption is required. But be careful. Because this repetition easily will become very annoying for the people it would be wise to guide through additional questions to point them to the expected behavior or to the obvious negative tests that would be needed. If the procedure does not seem to come to an end you should propose something by asking: What if we try this and that? In the end we want to write unit tests. We don't want to drive the developers crazy.

Especially when we talk about legacy code it is more like covering the existing code with negative tests for these are usually difficult to write as end-to-end tests if at all. This would add value to the existing test base and yields fast positive return on investment by disclosing possible fatal errors that would cause the application to crash. There never has been a session without at least one of these. In most cases these tests are quite easy to write and the bugs they are disclosing are often easy to fix. With that the developers would have a successful first unit test session. In later sessions the very same technique could be used to bring about the positive tests as well.

A checklist to summarize:

  • focuses on feature/behavior instead of algorithms and lines of code 
    • opens up your mind for new approaches
    • maybe the existing implementation could be improved?
  • forces to divide the feature into portions to be tested 
    • unveils structure of the feature at hand from an outside in point of view
    • discovers possible negative test opportunities instead of focusing on the happy path
  • annoys and breaks indifference
  • as any question forces the developer to come up with his own answers 
    • solution will be better accepted 
  • easily becomes too annoying and causes resistance 
    • guidance through hints or additional hint like questions required

Read also:

On Sporadics - How to deal with intermittent tests in continuous delivery

The opinions expressed in this blog are my own views and not those of SAP


Post a Comment