The purpose of unit testing is to assert the correct functionality of a small module of code, in isolation from the rest of the codebase. This can be challenge however when the method-under-test has dependencies on other parts of the project. That is, it calls out to code that is already tested elsewhere. Furthermore, these dependencies often form part of a broad dependency tree which may include not only Java classes but external resources such as databases, files or remote services.
To work around this problem, it is necessary to alter the code under test in some way as to remove these external dependencies. This is typically achieved by replacing the external dependencies with objects that provide the required behaviour for the method-under-test to function to a level sufficient that it can be tested.
Do not forget that Test Isolation is the key distinction between unit tests and integration or functional tests. In parts of your project that are highly dependent on external resources (data access objects or delegates for example,) unit tests can sometimes offer little value, as you are forced to stub out most of what the methods actually do. Such classes and methods should be tested using integration and/or functional tests which fall outside the scope of Sureassert UC.
Where to draw the line when considering test isolation can be a difficult decision, but generally it is best to focus on excluding code outside of the method-under-test that is either expensive (resource intensive; slow), or dependent upon external resources (generally via network or filesystem I/O of some kind).
One way of lessening the impact of external dependencies on unit testing is to use a dependency injection API such as Spring to wire together the components of your project. You can then create an alternate wiring configuration for your unit-testing context – your object-under-test is typically then retrieved from DI Factory which configures it with stubbed dependencies. Although this approach helps and is recommended, there are usually still times when the objects you need to unit test must be reconfigured in some way from the test itself, in order for it to be tested effectively.
Traditional unit testing approaches rely on programmatic configuration of stub and mock objects. Sureassert provides a number of options for stubbing declaratively, which are covered in this section.
- 5.1. Test Doubles
- 5.2. Method Stubs
- 5.3. Source Stubs
- 5.4. Expecting Stub Invocation: vstubs
- 5.5. Default Classes