Unit testing is an automatic tool that can catch regressions--unwanted changes in previously working code--introduced during development and refactoring. However, it won't guarantee the quality and correctness of your software.
Like most other things in software, unit tests can be badly written. The worst ones focus on a scenario that isn't relevant to the application. The best ones are written to catch corner cases or edge cases, such as "What happens if a negative value is entered as salary?" Having many unit tests isn't necessarily a sign of good code quality; the relevance of unit tests is what matters. There's no proven correlation between code coverage (the percentage of lines of code exercised by unit tests) and code correctness. Unit testing is just one of several tools developers use to improve code quality.
Code Contracts, which were introduced by Microsoft in the .NET framework 4, complement unit testing. They're an API through which you define preconditions, postconditions, and invariants for your classes. For critical methods on critical classes, a developer expresses conditions that must be verified for the method to run correctly (for example, a temperature can't be less than -273 degrees Celsius) and those that should occur when the method terminates. Code Contracts require code to be embedded in existing classes. It's up to developers to decide whether to keep this code in release builds or limit it to debug builds.
Code Contracts help ensure software correctness in three ways. First, they're part of the method implementation and contribute to ensuring the function abides by its expected public contract. Second, they force developers to think about what each method is expected to do and why. That provides guidance for maintaining the code. Finally, Contracts are a form of metadata, and the information they convey can be consumed by other code analysis tools such as Microsoft's static checker. The static checker is triggered by code changes and compile steps, and ensures that preconditions and postconditions match, and that invariants--conditions that must hold for a class at all times--are always met.
Unlike unit tests, Code Contracts enable static analysis, which can provide feedback as code is written. By comparison, a battery of unit tests requires several minutes to run. A static analysis tool doesn't give developers certainty, but it does provide useful information about the code being developed.
No external tool can validate the relevance of unit tests. Test results may or may not be successful, but there's no guarantee of their quality. With Code Contracts, everything is based on the contracts defined on a per-method basis. If the contracts are correct, and no warnings are generated from the static analysis, then the code is likely to be correct.
Note that correct doesn't mean bug-free. Correctness refers to the correct flow of actions and data, which contracts can verify. Implementation details are verified with unit tests. That's why unit tests and Code Contracts work well together.
It's also worth noting that Microsoft has released a smart unit test generator called Pex that understands contracts in the code and specializes in high code coverage and high relevance. Pex is a Visual Studio add-in and uses the same underlying technology as static analysis to "understand" the most relevant tests to be written for code. It should be carefully considered whenever unit tests or Code Contracts are in use.
Dino Esposito has written several programming books about Microsoft tech. Write to us at [email protected]