You can do things like set up scenarios you know won't work, such as connecting to machines that don't exist or sending the wrong parameters.
The main thing is if the external inputs can't be made to "violate a contract", the internals aren't the problem.
Ultimately, it depends on what you are building. Simple CRUD web applications are different from systems development type applications in many cases. Many of these classes can be more easily mocked out.
Often the breaking change you'll see is an upstream API or SaaS endpoint changing in ways you didn't expect (or the difference between a Linux utility on the latest Ubuntu, an older RHEL, and Solaris), so it's often better to concentrate on testing the real thing.
And these tests are expensive, so an automated test matrix that deploys a VM fleet with lots of permutations is usually necessary.
[0] Meditations by Sean Cassidy, http://blog.seancassidy.me/meditations.html
In these cases, whether you're PXE booting a server or talking to 500 different APIs that all range between the latest database or cloud API, flashing firmware on a RAID controller (shudder), whatever, there's a point of diminishing returns. There's also most of the bugs that you won't be able to simulate, because they come from external factors.
The magic lies in automated integration tests.
QA automation is AMAZING stuff. Set up real servers and real test environments when you can. Test real database upgrades.
Mock testing can have it's place, but often you find yourself making internal API contracts more rigid than you want, which can limit refactoring.