Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Beginning TDD - Challenges? Solutions? Recommendations? [closed]

People also ask

What were the challenges in starting implementing TDD?

The main problem with Test-Driven Development, is that unit testing is not a measure of correctness but a measure of predictable behavior. Unit tests guarantee that our code behaves as we expected it to, but the expected behavior might be incorrect, incomplete or functional only on happy flows.

What is the first step in test driven development cycle?

Five steps of test-driven developmentTranslate the requirement by writing a unit test. If you have hot reloading set up, the unit test will run and fail as no code is implemented yet. Write and implement the code that fulfills the requirement. Run all tests and they should pass, if not repeat this step.

What is the meaning of TDD?

Test-driven development (TDD), also called test-driven design, is a method of implementing software programming that interlaces unit testing, programming and refactoring on source code.


First, it is alright and normal to feel frustrated when you first start trying to use TDD in your coding style. Just don't get discouraged and quit, you will need to give it some time. It is a major paradigm shift in how we think about solving a problem in code. I like to think of it like when we switched from procedural to object oriented programming.

Secondly, I feel that test driven development is first and foremost a design activity that is used to flesh out the design of a component by creating a test that first describes the API it is going to expose and how you are going to consume it's functionality. The test will help shape and mold the System Under Test until you have been able to encapsulate enough functionality to satisfy whatever tasks you happen to be working on.

Taking the above paragraph in mind, let's look at your questions:

  1. If I am using a collection in my system under test, then I will setup an expectation to make sure that the code was called to insert the item and then assert the count of the collection. I don't necessarily test the Add method on my internal list. I just make sure it was called when the method that adds the item is called. I do this by adding a mocking framework into the mix, with my testing framework.
  2. Testing strings as output can be tedious. You cannot account for every outcome. You can only test what you expect based on the functionality of the system under test. You should always break your tests down to the smallest element that it is testing. Which means you will have a lot of tests, but tests that are small and fast and only test what they should, nothing else.
  3. There are a lot of open source testing frameworks to choose from. I am not going to argue which is best. Just find one you like and start using it.
    • MbUnit
    • nUnit
    • xUnit
  4. All you can do is setup your tests to account for what you want to happen. If a scenario comes up that introduces a bug in your functionality, at least you have a test around the functionality to add that scenario into the test and then change your functionality until the test passes. One way to find where we may have missed a test is to use code coverage.

I introduced you to the mocking term in the answer for question one. When you introduce mocking into your arsenal for TDD, it dramatically makes testing easier to abstract away the parts that are not part of the system under test. Here are some resources on the mocking frameworks out there are:

  • Moq: Open Source
  • RhinoMocks: Open Source
  • TypeMock: Commercial Product
  • NSubstitute: Open Source

One way to help in using TDD, besides reading about the process, is to watch people do it. I recommend in watching the screen casts by JP Boodhoo on DNRTV. Check these out:

  • Jean Paul Boodhoo on Test Driven Development Part 1
  • Jean Paul Boodhoo on Test Driven Development Part 2
  • Jean Paul Boodhoo on Demystifying Design Patterns Part 1
  • Jean Paul Boodhoo on Demystifying Design Patterns Part 2
  • Jean Paul Boodhoo on Demystifying Design Patterns Part 3
  • Jean Paul Boodhoo on Demystifying Design Patterns Part 4
  • Jean Paul Boodhoo on Demystifying Design Patterns Part 5

OK, these will help you see how the terms I introduced are used. It will also introduce another tool called Resharper and how it can facilitate the TDD process. I couldn't recommend this tool enough when doing TDD. Seems like you are learning the process and you are just finding some of the problems that have already been solved with using other tools.

I think I would be doing an injustice to the community, if I didn't update this by adding Kent Beck's new series on Test Driven Development on Pragmatic Programmer.


From my own experience:

  1. Only test your own code, not the underlying framework's code. So if you're using a generic list then there's no need to test Add, Remove etc.

  2. There is no 2. Look over there! Monkeys!!!

  3. NUnit is the way to go.

  4. You definitely can't test every outcome. I test for what I expect to happen, and then test a few edge cases where I expect to get exceptions or invalid responses. If a bug comes up down the track because of something you forgot to test, the first thing you should do (before trying to fix the bug) is write a test to prove that the bug exists.


My take on this is following:

  • +1 for not testing framework code, but you may still need to test classes derived from framework classes.
  • If some class/method is cumbersome to test it may be strong indication that something is wrong with desing. I try to follow "1 class - 1 responsibility, 1 method - 1 action" principle. That way you will be able to test complex methods much easier by doing that in smaller portions.
  • +1 for xUnit. For Java you may also consider TestNG.
  • TDD is not single event it is a process. So do not try to envision everything from the beginning, but make sure that every bug found in code is actually covered by test once discovered.

I think the most important thing with (and actually one of the great outcomes of, in a somewhat recursive manner) TDD is successful management of dependencies. You have to make sure that modules are tested in isolation with no elaborate setup needed. For example, if you're testing a component that eventually sends an email, make the email sender a dependency so that you can mock it in your tests. This leads to a second point - mocks are your friends. Get familiarized with mocking frameworks and the style of tests they promote (behavioral, as opposed to the classic state based), and the design choices they encourage (The "Tell, don't ask" principle).