(PUP-3621) Ensure that get! caches

The introduction of get! caused any users of that method to circumvent the
cache. This happened because, rather than delegating to get() and raising
an error, it implemented similar logic to get(), but only from the common
base class. This caused it to skip all caching checks. This adds caching
back in and adds tests for that behavior.

In the line of fixing the bug, there was a fair amount of refactoring around
the tests and some changes to the structure of the Cached loader.