Test-Driven DevelopmentOr The Best Thing Since Sliced Bread James Kirkbride
OverviewTest-Driven Development (TDD):• A method in which you write the test FIRST, and then write production code.• Tests determine how the production code will be written.• A short cycle of steps are used to write clean, tested, testable code which can be REFACTORED more safely.
Technical Benefits Smaller methods. Simplifies the code. Encourages separation of concerns. Confidence that changing the code will not break something else. Reduced time in rework/refactoring. Short feedback loop.
Non-Technical Benefits Increased confidence that the application works as expected. Decreased time/effort for production support. Decreased time/effort for maintenance. Decreased ramp-up time for new developers.
SO HOW DOES IT WORK?
Tests + Production CodeClick icon to add + Refactoringpicture = Voltron
Red, Green, Refactor1. Write a test.2. Run tests, watch the test FAIL!3. Write just enough code to pass the test.4. Run tests, check that the test PASSED.5. REFACTOR the code.6. Run tests, check that tests still PASS.7. Start over again.
Red, Green, Refactor
Types of Tests Unit Tests Only concerned about the current method/function within the current “layer of abstraction.” No database interactions. Should run in less than a second per test. May require mocking out dependencies (services, other method calls, etc). Integration Tests Concerned with all “layers of abstraction” up to the current method/function. Includes database interactions. Should run in only a couple of seconds per test. Only mocks external dependencies (external web services, RMI calls, etc). Functional Tests “End-To-End” tests which include the full stack. Typically requires a fully integrated environment. Time-intensive (several seconds per test). Little to no mocking when possible.
Testing Techniques Dependency Injection Used to achieve “Inversion of Control” (IoC) Hides implementation behind contracts Allows mock objects to be injected into tests Encourages separation of concerns by delegating execution of tasks Mocks Objects or methods which simulate expected behavior Used in unit tests to simulate dependencies Should only be used to test functionality other than the mocked behavior Stubs Objects or methods which re-implement behavior Used in unit tests to assist with the completion of a test Should only be used to test functionality other than the stubbed behavior Factories Used to create objects in a consistent manner Reduces “clutter” in test method by abstracting the details of object creation away Useful for complex objects or large object graphs
Testing Techniques (cont.) Think small Small, concise methods are easier to understand, maintain, and test Small, concise tests are easier to understand and maintain Use descriptive test names Aids in understanding what the test is trying to prove “testValidateShouldReturnAnErrorWhenIdIsMissing” is more descriptive than “testIdMissing” Learn to refactor like a pro Use your IDE’s built in refactoring tools (Extract method, extract class, extract interface, introduce variable, introduce constant, etc) Read up. Working Effectively with Legacy Code by Michael C. Feathers Clean Code by Robert “Uncle Bob” Martin Read other people’s code and tests Get a different viewpoint Find the flaws/benefits in how they write code Grow as a developer!
TDD is a MindsetIt is about MORE THAN CODE It is about ATTITUDE
Myths and Misconceptions “It takes too long to write tests.” Time spent testing up front reduces time spent on maintenance and production support later. “You should never use mocks, and only test with the real implementation.” Used judiciously mocking allows you to test at a given layer of abstraction, and to ignore everything that is not related to the code currently under test. “We’re in a time crunch, forget the tests.” Sacrificing quality for quantity will cost more in the long run. “Writing tests first is overkill. Writing them afterwards is good enough.” Writing tests after the fact is not testing. It is recording behavior. The purpose of writing tests first is in part to flush out bugs early, and partly to insure you only write the code you need. “We need 100% test coverage for TDD to work.” Aside from being almost impossible, 100% test coverage is not necessary. Usually 85% coverage is “enough”, but it is more about “what” and “how” you are testing versus “how much.” “Writing and maintaining tests is hard.” This can be true, but the value they provide is worth more than the effort it takes to write and maintain them (or it *should* be).
Reading Material Working Effectively with Legacy Code – Michael C. Feathers Amazon - http:// www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052/ref=sr_ ISBN-13 - 978-0131177055 Clean Code – Robert C. Martin Amazon - http:// www.amazon.com/gp/product/0132350882/ref=s9_simh_gw_p14_d19_g14_i3?pf_rd_m=A ISBN-13 - 978-0132350884