Unit tests are quick to run, quick to write in bulk, target a small area of code, are not prone to timing issues and other intermittent failures and have excellent reporting tools.
However, actual usage is often prevented by side effects which require resources such as processes, ports and ets tables to exist or will crash the test.
Separating code into pure and impure functions can help and is typically beneficial. It can also harm readability, and even then not enable full coverage using unit testing. For example:
receive_message(Msg) ->