From the article: Because you want to ensure that you always pass the majority of tests, you tend to think about this when you change and extend the program. You therefore are more reluctant to make large-scale changes that will lead to the failure of lots of tests. Psychologically, you become conservative to avoid breaking lots of tests. Interesting. I've often found that the lack of tests leaves me absolutely terri…
One good way to the limit breakage in such cases is to solely perform black box tests on the API level. In case of our Node.js based Backends we don't ever write a single classical unit test, instead we have a custom Framework built on top of Mocha which performs tests on the HTTP layer against all of our endpoints. This works remarkable well in practice and allows for large scale refactorings under the hood with lit…
I took this approach on several different projects, but I figured that a lot of the boilerplate/infrastructural code that you need to actually write these kinds of tests is poor or simply not available.
For example, declaratively starting and running multiple services together, in parallel and at the right time (with service dependencies) and printing their logs out.
Or, mocking the forward passage of time. (click something -> move forward a week -> click something else).
Or, asynchronously 'listening' using epoll for emails on a mock SMTP server that logs out the emails it receives.
Selenium's good for the web interaction stuff, but you need much much more than that to be able to effectively test at this level.
I think this kind of testing would be much more widely used and effective if the tools available were up to scratch.