I don't do unit testing very much, except for critical and complex functions. Unit tests result in a very high change impedance.
For complex code, what I prefer to do is define (1) the algorithm and (2) the structures, and then mentally verify the algorithm. Once mentally verified, I then translate it into code. Using that process, I have never had a single defect in code written that way, and the code is highly maintainable, and amenable to change, becasue I preserve the algorithm specification in the code's README page.
The goal is for code to be reliable and maintainable. There are many ways to achieve that.
Also, today's applications tend to be highly distributed. That means that integration tests are the most important of all.