Tuesday, September 1, 2015

13 steps to make TDD actually work

Why TDD doesn't work for you?

This is one of those situations where being brutally with yourself will help.  Test Driven Development (TDD) does not work because you are not doing it.  It is as simple as that.  You gave up on TDD because it was it was sounded good but it was hard.


What is TDD?

At its core TDD is thinking about your unit tests before you write the implementation code.  When you write the code first you are likely focusing on one happy path end result and coding to try to reach generate that result.  That closes your mind off to all the possible outcomes, all the possible input scenarios, and all the possible things that could go wrong.  So in TDD you think about the tests first so you do not have blinders on to scenarios beyond the happy path through your code.


What is so appealing about TDD?

When you were first introduced to TDD you were likely shown a hello world like feature then you were told how to write the tests for it and then shown you write the code to make your test pass a little at a time.  It was glorious.  The solution to better tests was just writing your tests first! Your tests could be better by thinking about everything and not just the happy path.  It is such a simple concept you can do that! No problem.  Less bugs means more time doing fun coding and less time doing support work.


So why aren't you doing TDD now?

You ran off to your project base code and tried to implement the feature using TDD and it was not as easy as that hello world example.  You tests required mock objects which you had to just guess about it because you were not sure exactly how you would implement the code.  After some struggles you either just gave up because you had a deadline and this was slow or you banged out some hard won tests and started on the "real" functional coding.  After you wrote some real code you figured out that tests you guessed about still weren't passing because the guesses you made about the mock objects and service calls were just tiny bit off so you needed to fix those to even some of your tests to pass.  How is disheartening! You spent all that time writing those tests first and they were not really adding value and you weren't "writing your tests first."  So you gave up on TDD.  It was hard. It wasn't the solution to your problems like you were told and all it did was slow you down.


Why should you give TDD another chance?

Because you were doing it the hard way and you didn't even know it.  Doing TDD is a mindset which you aren't going to develop just because you watched one presentation about it and tried it once.  You were sold on the concept but it practice it was hard.  TDD is not as black and white, not as all or nothing, as you were told.


How should you being doing TDD:

  1. Get your feature ticket from Jira, a post it note, or wherever
  2. Do not open up your IDE
  3. Think about the feature and do a quick design of it either in your head or on a whiteboard
  4. Write down some test scenarios
  5. Now open up your IDE
  6. Create your "real code" class and method signature
  7. Create your unit test class
  8. Create a unit test but instead of writing the test case just stub out the unit tests and create comments about what the test scenario you want to cover (see sample below)
    • Include both the input, expected results
  9. If possible write the assert statement for those expected results
  10. Repeat steps 8 and 9 until you have covered every scenario you can think of
  11. Go write your "real code"
    • pause occasionally if you think of a new scenario and update the unit test class to include it
  12. Now fill in the unit test to cover those test scenarios including all the inputs, mock objects, and even better assert statements
  13. Tell your coworker about how you actually did TDD and it made your code better


Sample Stubbed out Test Class

public class AdditionHelperTest {

 /**
  * test basic addition
  *
  * input: 1, 2
  * 
  * expected: output 3
  * 
  */
 @Test
 public void testAdditionHappyPath() {
  fail("Not yet implemented");
 }

 /**
  * test null input
  *
  * input: null, 2
  * 
  * expected: output illegal argument exception
  * 
  */
 @Test
 public void testAdditionNullFirstArg() {
  fail("Not yet implemented");
 }

 /**
  * test null input
  *
  * input: 1, null
  * 
  * expected: output illegal argument exception
  * 
  */
 @Test
 public void testAdditionNullSecondArg() {
  fail("Not yet implemented");
 }

 /**
  * test negative numbers addition
  *
  * input: 1, -2
  * 
  * expected: output -1
  * 
  */
 @Test
 public void testAdditionNegative() {
  fail("Not yet implemented");
 }

 /**
  * test addition large numbers
  *
  * input: Integer.MAX_VALUE, Integer.MAX_VALUE
  * 
  * expected: exception
  * 
  */
 @Test
 public void testAdditionLargeNumbers() {
  fail("Not yet implemented");
 }
}


What is next?

As you get better at thinking about TDD you may be able to create more of the test code first, for some features, and you will get faster at generating those test scenarios.  You will have less bugs in your code. You will then have more time to do fun new development and spend less time doing boring production support.

Other Testing Magic:

1 comment:

  1. I tend to agree with you. I think a lot of people jump to the editor too quickly. And as a result, spend more time typing and less time thinking.

    I used to be one of these people. Jump into the editor and start writing tests while I practice Test Driven Design.

    Now, I like to do something similar to what you describe. Grab a story, think about it, design something that I think solves the problem, create some test cases, and then program it. It is a much better flow for me.

    The difference in the way I used to practice TDD compared to this flow is that by the time I get to write my tests, I already know what my classes and methods will look like.

    I think this provides a much better solution and fewer bugs slip through the cracks.

    ReplyDelete