List of Figures

Chapter 1. The basics of unit testing

Figure 1.1. In classic testing, developers use a GUI (graphical user interface) to trigger an action on the class they want to test. Then they check the results.

Figure 1.2. You can have many failure points in an integration test. All the units have to work together, and each of them could malfunction, making it harder to find the source of the bug.

Figure 1.3. The traditional way of writing unit tests. The dotted lines represent actions people treat as optional.

Figure 1.4. Test-driven development—a bird’s-eye view. Notice the spiral nature of the process: write test, write code, refactor, write next test. It shows the incremental nature of TDD: small steps lead to a quality end result.

Chapter 2. A first unit test

Figure 2.1. Unit tests are written as code, using libraries from the unit-testing framework. Then the tests are run from a separate unit-testing tool, and the results are reviewed (either in the UI or as text) by the developer or an automated build process.

Figure 2.2. The NUnit GUI is divided into three main parts: the tree listing the tests on the left, messages and errors at the top right, and stack trace information at the bottom right.

Figure 2.3. NUnit test failures are shown in three places: the test hierarchy on the left becomes red, the progress bar at the top becomes red, and any errors are shown on the right.

Figure 2.4. NUnit performs setup and teardown actions before each and every test method.

Figure 2.5. How NUnit calls SetUp and TearDown with multiple unit tests in the same class: each test is preceded by running SetUp and followed by a TearDown method run.

Figure 2.6. In NUnit, an ignored test is marked in yellow (the middle test), and the reason for not running the test is listed under the Tests Not Run tab on the right.

Figure 2.7. You can set up categories of tests in the code base and then choose a particular category to be run from the NUnit GUI.

Chapter 3. Using stubs to break dependencies

Figure 3.1. Our method has a direct dependency on the filesystem. Our design of the object model under test inhibits us from testing it as a unit test; it promotes integration testing.

Figure 3.2. A space shuttle simulator has realistic joysticks and screens to simulate the outside world.

Figure 3.3. Introducing a layer of indirection to avoid a direct dependency on the filesystem. The code that calls the filesystem is separated into a FileExtensionManager class, which will later be replaced with a stub in our test.

Figure 3.4. Introducing a stub to break the dependency

Figure 3.5. Flow of injection via a constructor

Figure 3.6. Using properties to inject dependencies. This is much simpler than using a constructor because each test can set only the properties that it needs to get the test underway.

Figure 3.7. A test configures the factory class to return a stub object. The class under test uses the factory class to get that instance, which in production code would return an object that isn’t a stub.

Figure 3.8. We inherit from the class under test so we can override its virtual factory method and return whatever object instance we want, as long as it implements IExtensionManager. Then we perform our tests against the newly derived class.

Figure 3.9. Using Extract and Override to return a logical result instead of calling an actual dependency. This uses a simple fake result instead of a stub.

Chapter 4. Interaction testing using mock objects

Figure 4.1. When using a stub, the assert is performed on the class under test. The stub aids in making sure the test runs smoothly.

Figure 4.2. The class under test communicates with the mock object, and all communication is recorded in the mock. The test uses the mock object to verify that the test passes.

Figure 4.3. Our test will create a MockWebService to record messages that LogAnalyzer will send. It will then assert against the MockWebService.

Figure 4.4. LogAnalyzer has two external dependencies: web service and email service. We need to test LogAnalyzer’s logic when calling them.

Figure 4.5. The web service will be stubbed out to simulate an exception; then the email sender will be mocked to see if it was called correctly. The whole test will be about how LogAnalyzer interacts with other objects.

Chapter 5. Isolation (mock object) frameworks

Figure 5.1. The web service will be stubbed out to simulate an exception, and the email sender will be mocked to see if it was called correctly. The whole test will be about how LogAnalyzer interacts with other objects.

Figure 5.2. Usage of isolation frameworks among my blog readers

Chapter 6. Test hierarchies and organization

Figure 6.1. Integration tests and unit tests can reside in different folders and namespaces but remain under the same project. Base classes have their own folders.

Figure 6.2. The unit-testing and integration projects are unique for the LogAn project and have different namespaces.

Figure 6.3. One base class with a common setup method, and two test classes that reuse that setup method

Figure 6.4. A template test pattern ensures that developers don’t forget important tests. The base class contains abstract tests that derived classes must implement.

Figure 6.5. A typical inheritance hierarchy that we’d like to test includes an abstract class and classes that derive from it.

Figure 6.6. A standard test class hierarchy implementation. Most of the tests are in the base class, but derived classes can add their own specific tests.

Chapter 8. Integrating unit testing into the organization

Figure 8.1. An example of a test-code-coverage report

Figure 8.2. An example test-code-coverage trend report

Chapter 9. Working with legacy code

Figure 9.1. Mapping components for test feasibility

Figure 9.2. Easy, hard, and irrelevant component mapping based on logic and dependencies

Figure 9.3. When starting with the easy components, the time to test gets longer and longer until the hardest components are done.

Figure 9.4. When you use a hard-first strategy, the time to test is long for the first few components, and then it gets shorter as more dependencies are refactored away.

Figure 9.5. Depender is simple and easy to use.

Figure 9.6. Using FitNesse for integration

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.7.208