The canonical example in my mind is any kind of autofix-able linter, where there's some kind of patch (or more nuanced autofix) that the linter can generate on-the-spot for you. With a sh_test construct (or any other test), you generally find yourself printing out some command that the user can run to fix things, which in a sufficiently large codebase can get really frustrating. (The worst offenders of this that I remember dealing with at Google were when generated code/artifacts had to be checked into the repo instead of getting being wired into the build system because <insert hermeticity problem> and a large-scale change you were doing looked at one of them the wrong way...)
(My company - https://trunk.io - is actually building a universal linter as part of our product offering, and we already have a system to write custom linters with varying levels of sophistication that can plug into both your IDE and CI system!)