Unit Testing? Consider Taking a Rain Check
Unit testing is a great way to verify software at an early stage and to ensure that modified functions are still working as specified. Unit tests can also help drive development, essentially working as a specification-by-example technique. Unit tests usually work for individual methods in a class, and for each execution of a unit, test a set up and tear down of an instance of the class of which the methods are members.
However, unit tests are not a magic wand. Many of them will typically be low-level routine tests, calling function (method) and checking the result of that function. However, if the function under test has complex dependencies, it will take more effort to create tests for it. The dependencies can be on other functions or even on other components or external services. It can also require certain values in a database and/or modifying them. The effort to establish the proper pre-conditions for a test at unit level might not be worth it.
Rather than making such a big effort, consider taking "rain checks" for certain tests. Test the aspect that can be tested immediately, but save some of the complex testing for later. Achieve the testing goals by higher level tests at the level of components, services, or even the UI.
In this approach, you would analyze for a function what test situations are needed to test it. Then, you would establish the closest higher level at which those situations can be created more easily. This might often be the component level, but it could also be the UI level or in interoperability testing. In the test design for the chosen level, you make sure that the test situations for the units have been covered. This will ensure that the function at unit level has been exposed to the situations necessary to test it.
Making sure the needed situations are covered at a certain test level does not necessarily mean each function gets its own test case there. The verification can also be part of a larger comprehensive scenario. This is probably even better in most situations, so tests don't get too granular.
A major advantage of unit tests is their ability to verify code changes. They are closely related to the code and get executed whenever there are changes to that code. In a rain check approach, this link with the code must be kept intact. In the Action Based Testing method, this can be done via an entity called "test objective". Test objectives are natural language statements that can link the test cases to other items—in this case code files or functions.
Unit tests are a beneficial part of system development processes. They allow the teams to build automated verification that keeps up with the code. With a rain check approach, the advantages of unit testing can be supported by the levels of tests where it is most economical to do so. It is up to the team to decide in which cases, if any, this can make sense.