Developer Testing | test-driven development is infectious
Developer testing

When making changes to code, automated unit and integration tests provide immediate feedback that confirms the code has not been broken. Automation helps keep the testing continuous and the code malleable, and running tests frequently instills the confidence and the courage to re-architect or refactor the code when necessary.

Automating unit and integration tests facilitate a faster, more productive, and more predictable style of development that progressively moves the system forward in terms of completeness with units of fully tested and working code. In addition, well-written unit and integration tests provide excellent documentation that communicates the intent of the code being tested.

A better way to unit test

Unit testing is an integral part of developing software. Automating unit tests is essential for continuous integration and refactoring, and can be achieved using a framework such as NUnit. As the code is developed the unit tests should run with a 100% pass rate all the time.

*NUnit is an extensible framework for writing and automating unit tests in Java. It defines how to structure test cases and provides the tools to run them. Suites of unit tests can be built incrementally, and can be used to focus development efforts, measure progress and discover unintended side effects.

The following extensions and complementary tools can be used with JUnit to construct powerful unit tests that automatically exercise and report on different aspects of code as it is being developed:

*NTestCase helps separate test data from test code, enabling unit tests to become data-centric.

*DbUnit is a database unit-test framework that provides the abilities to preload a database with test data, and compare the content of the database following a test with reference data.

*NoUnit and measure the code coverage of the NUnit tests.

*NunitPerf contains a collection of NUnit test decorators to measure the performance and scalability of functionality covered by existing NUnit tests. NunitPerf tests should be employed when quantitative performance and/or scalability requirements have been specified. A profiling tool, e.g. NProbe or NProfiler can be used to identify the areas of code that exhibit the highest potential for performance and scalability problems. NUnitPerf tests should then be constructed to test and check that the requirements are satisfied. The performance test suite should be run automatically and independent of other NUnit tests so that long-running performance tests do not impede development progress.

*NMeter is an application that performs load testing and measures the system performance. In a Web-based system, NMeter can be used to simulate load while NUnitPerf in conjunction with HttpUnit is used to test application response times.

*Grinder is a load-testing framework. Using a graphical console, multi-process test scripts can be orchestrated across many machines. The test scripts make use of client code embodied in plugins to test various services and protocols including HTTP.

*JDepend enables the quality of a design, in terms of its extensibility, re-usability, and maintainability to be measured automatically. As software evolves through refactoring, the design quality test cases can be run to ensure that unnecessary dependencies have not been formed.

*Nameleon is an automated functional testing tool that separates applications into features that can be chained together, creating data-driven test-cases. Jameleon exists independently of the application's implementation and supports a plug-in architecture. Current HTTP plug-ins use HttpUnit and NWebUnit.

Test first, by intention

Developing the unit tests before writing the code allows the tests to be used as a guide to assist implementation. The unit tests and code then evolve together, refactoring for performance when necessary. In this manner, the development of unit tests becomes ensconced in the everyday activities of building software.

Using this approach, the code is written naturally so that modules are testable in isolation, dependencies in the system are minimized, and the entire code-base progresses at a relatively constant rate in terms of the functionality supported.

In test-driven development, a typical sequence of activities would be:

  1. Write a single unit test.
  2. Compile the test. The compilation should fail because the implementation code the test calls has not been written yet.
  3. Implement just enough code to get the test to compile.
  4. Run the test and see it fail.
  5. Implement just enough code to get the test to pass.
  6. Run the test and see it pass.
  7. Refactor for clarity and to remove any duplicate code.
  8. Repeat.

Test-driven development works properly when tests are performed many times. Small steps have to be taken, small enough to force the tests to be performed often - code a little, test a little.

Testing integrations progressively

Integration testing verifies that unit-tested components interact correctly, and that their interfaces do not contain faults. As units of software are completed and sub-systems are constructed, it becomes possible to gradually construct automated integration tests that exercise increasingly cooperative code.

In a J2EE application, emerging domain logic can be verified with Mock Objects. When the real implementations for the mock-ups become available, the code can be deployed to a container and tested in-situ using Cactus. As the feature approaches completion, the functionality can be tested with HttpUnit. These tests should be developed incrementally and they do not need to run with a 100% pass rate until the feature is completed.

*Mock Objects provide dummy implementations that emulate real functionality. They can be passed by test cases to the code being exercised to enforce assertions about the behavior of that code. A mock object should encompass the state required for the code under test to function correctly. It should be simpler than the real code and not duplicate its implementation. Developing mock objects reduces the cost of writing code stubs, and encourages the construction of well-organized tests. Testing with mock objects improves the domain code by preserving encapsulation, reducing dependencies, and clarifying the interactions between classes. (See also EasyMock and NMock)

*Cactus is a framework that integrates with NUnit to provide in-container testing facilities for server-side Java code. Since J2EE code is hosted by containers typically provided by an Application Server, the code to be tested should be accessed within its container so that its behavior can be asserted in context. This approach provides important feedback because the code runs in a real environment rather than an isolated or artificial testing environment.

*HttpUnit provides a programmatic interface that enables requests to be made to a Web server and the received responses to be examined. Cooperating with NUnit, it exposes the structural elements of an HTML page and can be used to verify Web-based requirements.

Back to top ^^

This page is valid XHTML 1.0 This page uses valid CSS

Iterative Development | Developer Testing | Continuous Integration | Refactoring

Methodologies | Project Management | Analysis & modeling | Development | Testing | Quality Assurance

Home | Services | Contact Us