User:Legaia/Sandbox

Why TDD
The basic idea behind good test driven development is to prioritize making code right before making it good. This isn't to say we will never have time to write good code, but we focus our energies towards solving the problem correctly first. Isn't that the whole goal at the end of the day? If it doesn't work, it doesn't matter if it's elegant! With that in mind, you're going to (1) Write a test (2) Make it run, then (3) Make it right.

If you still remain unconvinced, there are other reasons for test before, and for test at all (for the cowboys out in the crowd). When we write tests as we go, we prove our code works as we go. The tests represent assumptions we've made about valid inputs and outputs and if they are succeeding, they represent proof that our assumptions still hold true. As we add new features or refactor existing code, the old tests automatically regress our past assumptions, but with our current feature set. And handily, when you create a bug, especially when you accidentally break existing code, the tests will tell you right away. If you run your tests often enough, you'll know right away which lines of code introduced the bug, because you just wrote them. This is so much more effective than writing tests 2 weeks later and having to rehash "Now what was I thinking here?"

Tests before also helps reduce "fluff" architecture, code for code's sake. If you only write code to help pass a test or remove duplication, you'll find yourself only adding architecture as it's justified. Thus you'll add designs organically as they're needed rather than on intuition about best practices. My favorite part of all, whenever our tests are passing, we know we have produced something. It may not yet be 100% of whatever the customer/business was asking for, but it's a set of functionality that actually works. With this in mind, it's possible to release stable functionality on a very short time scale, potentially on a daily basis.

Assumptions that make TDD work
Agile, like any other methodology, is not the golden ticket to success in and of itself. It takes a team that's educated in how to make it work, and supportive to help work through problems. This discussion won't go through all of the details of Agile, but these are the basic assumptions that you need to support a healthy TDD environment.

Communication between developers is cheap
Learning to be effective with TDD should not be expected to be learned overnight. Good communication especially between developers with experience writing tests and TDD to those who may be newer is critical. Questions come up, such as how do you write a test for an abstract class? How do you fake a database connection? How do you decouple two classes?

Communication with the customer is cheap
Developers are documenting assumptions about the requirements as they go. When they are blocked by a question, if the customer isn't available to unblock them, the developer either has to guess the right answer or work stops on that feature. The implications of having easy communication channels to the customer are obvious. No one will ever write the perfect requirements document such that questions will never come up. Even if they could - how much money would it cost to write such a document, and what would be the ROI, versus the time it would just take to ask some questions?

Refactoring is cheap
When you have regression tests, refactoring is cheap. You can confidentially make a little change, even if the implications are good if you have confidence that your tests are rigorously testing your code. TDD also focuses refactoring efforts primarily on removing duplication which is a simple task.

Testing is cheap
Automated tests are very fast in comparison to manual tests. Well written unit tests can run through hundreds or even thousands of scenarios in seconds and minutes, compared to the hours it would take in manual testing. Essentially, you can run your tests as often as you like, because the overhead is so near to zero. Plugins like Infinitest make this even easier by automatically executing your unit tests as you write code, similar to compilation as you type.

No Tests

 * Anti-pattern

Use TDD or code coverage to identify and generate reports for untested code.
 * Solution

Always fails

 * Anti-pattern

Use TDD or code coverage to identify and generate reports for untested code.
 * Solution

JUnit 3 confusion (forgot the annotation)

 * Anti-pattern

AssertionFailedError: No tests found in TestDog


 * Solution

Test via debugger

 * Anti-pattern


 * Solution

Test via logging

 * Anti-pattern


 * Solution

Multiple asserts

 * Anti-pattern

Tests should always have an assert/fail statement (PMD).


 * Solution


 * JUnit is designed to work best with a number of small tests. It executes each test within a separate instance of the test class. It reports failure on each test. Shared setup code is most natural when sharing between tests. This is a design decision that permeates JUnit, and when you decide to report multiple failures per test, you begin to fight against JUnit. This is not recommended.

Redundant assertion
UnneccessaryBooleanAssertion rule (PMD)

Superficial coverage (edge cases)
Code coverage tools like EclEmma for Eclipse and Cobertura for Maven can help you identify areas that need additional tests.

Integration tests as unit tests
If you have external dependencies, you have created an integration test not a unit test. The following are some examples of such dependencies:
 * Specific date or time
 * Third-party library jar
 * File
 * Database
 * Network connection
 * Web container
 * Application container
 * Randomness

FAQ

 * Do you have to write a test for everything? :No, just code that might reasonably break. Especially if you get stuck in a test-after situation, such as adding tests for legacy code, look for the highest ROI. Try to focus your efforts on the most complex and the most critical code.
 * How simple is "too simple to break?" :If it can't break on its own, it's too simple to break.
 * Why test before and not just test after? :You can find a more extensive answer above. The main points are (a) decrease over-designed code, (b) provide more stable builds (c) constant regression (d) immediate feedback when bugs are introduced, and (e) features are developed iteratively on a short time scale.
 * I have a deadline. How do I find time to write tests? :Potentially this is a discussion point where you will need support from your management chain. However, when you stick to the facts, using TDD saves money in the life of the project, even on the first project you use it. The most expensive part of software development is the maintenance cost. Good testing and good design are our best weapons to combat maintenance cost. Another hard fact to keep in mind is that bug detection becomes dramatically more expensive the later the bugs are found in the SDLC. Bottom line - TDD reduces the total cost of ownership.
 * How do I integrate JUnit? :Automated build processes such as ANT and Maven have JUnit support. Eclipse and IntelliJ editors have native JUnit support as well as plugins such as Infinitest to continuously run your tests. There are additional reporting tools such as EMMA and Cobertura that do line by line analysis to help identify coverage and code complexity. Also mentioned above, PMD has rule sets available to help you avoid common misteps like the anti-patterns listed above.