Chapter 1. The basics of unit testing
1.1. Unit testing—the classic definition
1.2. Properties of a good unit test
1.3.1. Drawbacks of integration tests compared to automated unit tests
1.4. Good unit test—a definition
2.1. Frameworks for unit testing
2.2. Introducing the LogAn project
2.4.2. Running our first test with NUnit
Chapter 3. Using stubs to break dependencies
3.2. Identifying a filesystem dependency in LogAn
3.3. Determining how to easily test LogAnalyzer
3.4. Refactoring our design to be more testable
3.4.1. Extract an interface to allow replacing underlying implementation
3.4.2. Inject stub implementation into a class under test
3.4.3. Receive an interface at the constructor level (constructor injection)
3.5. Variations on refactoring techniques
3.6. Overcoming the encapsulation problem
3.6.1. Using internal and [InternalsVisibleTo]
Chapter 4. Interaction testing using mock objects
4.1. State-based versus interaction testing
4.2. The difference between mocks and stubs
4.3. A simple manual mock example
4.4. Using a mock and a stub together
4.6. Stub chains: stubs that produce mocks or other stubs
Chapter 5. Isolation (mock object) frameworks
5.1. Why use isolation frameworks?
5.2. Dynamically creating a fake object
5.2.1. Introducing Rhino Mocks into your tests
5.2.2. Replacing a handwritten mock object with a dynamic one
5.3. Strict versus nonstrict mock objects
5.4. Returning values from fake objects
5.5. Creating smart stubs with an isolation framework
5.6. Parameter constraints for mocks and stubs
5.6.1. Checking parameters with string constraints
5.6.2. Checking parameter object properties with constraints
5.7. Testing for event-related activities
5.7.1. Testing that an event has been subscribed to
5.8. Arrange-act-assert syntax for isolation
5.9. Current isolation frameworks for .NET
5.10. Advantages of isolation frameworks
5.11. Traps to avoid when using isolation frameworks
5.11.2. Verifying the wrong things
Chapter 6. Test hierarchies and organization
6.1. Having automated builds run automated tests
6.1.1. Anatomy of an automated build
6.2. Mapping out tests based on speed and type
6.2.1. The human factor of separating unit from integration tests
6.3. Ensuring tests are part of source control
6.4. Mapping test classes to code under test
6.4.1. Mapping tests to projects
6.5. Building a test API for your application
6.5.1. Using test class inheritance patterns
Chapter 7. The pillars of good tests
7.1. Writing trustworthy tests
7.1.1. Deciding when to remove or change tests
7.1.2. Avoiding logic in tests
7.2. Writing maintainable tests
7.2.1. Testing private or protected methods
7.2.3. Using setup methods in a maintainable manner
7.2.4. Enforcing test isolation
7.2.5. Avoiding multiple asserts
7.3.3. Asserting yourself with meaning
Chapter 8. Integrating unit testing into the organization
8.1. Steps to becoming an agent of change
8.1.1. Be prepared for the tough questions
8.2.1. Guerrilla implementation (bottom-up)
8.2.2. Convincing management (top-down)
8.2.3. Getting an outside champion
8.2.4. Making progress visible
8.3.1. Lack of a driving force
8.3.2. Lack of political support
8.4. Tough questions and answers
8.4.1. How much time will this add to the current process?
8.4.2. Will my QA job be at risk because of this?
8.4.3. How do we know this is actually working?
8.4.4. Is there proof that unit testing helps?
8.4.5. Why is the QA department still finding bugs?
8.4.6. We have lots of code without tests: where do we start?
8.4.7. We work in several languages: is unit testing feasible?
8.4.8. What if we develop a combination of software and hardware?
8.4.9. How can we know we don’t have bugs in our tests?
8.4.10. My debugger shows that my code works: why do I need tests?
Chapter 9. Working with legacy code
9.1. Where do you start adding tests?
9.2. Choosing a selection strategy
9.3. Writing integration tests before refactoring
9.4. Important tools for legacy code unit testing
9.4.1. Isolate dependencies easily with Typemock Isolator
9.4.2. Find testability problems with Depender
9.4.3. Use JMockit for Java legacy code
9.4.4. Use Vise while refactoring your Java code
9.4.5. Use FitNesse for acceptance tests before you refactor
9.4.6. Read Michael Feathers’ book on legacy code
9.4.7. Use NDepend to investigate your production code
9.4.8. Use ReSharper to navigate and refactor production code
Appendix A. Design and testability
A.1. Why should I care about testability in my design?
A.2. Design goals for testability
A.2.1. Make methods virtual by default
A.2.2. Use interface-based designs
A.2.3. Make classes nonsealed by default
A.2.4. Avoid instantiating concrete classes inside methods with logic
A.2.5. Avoid direct calls to static methods
A.2.6. Avoid constructors and static constructors that do logic
A.3. Pros and cons of designing for testability
Appendix B. Extra tools and frameworks
B.3.5. Common Service Locator Library
B.4.1. Use integration tests for your data layer
Appendix Test Review Guidelines
3.145.70.170