If I understand it correctly, in classic 3-tier/n-tier architecture the goal is to ultimately separate responsibilities in such a way that each layer shouldn't have to know about what is going on/being used internally in lower tiers.
However, if the objects in each tier (especially business) are structured to be testable, their dependencies are defined as part of their public contracts (when testing a 2nd-tier object with a 3rd-tier object dependency, mock/stub out the 3rd-tier object and provide it to the 2nd tier object). This means that at implementation time, the first tier is responsible for grabbing a third-tier dependency for use in constructing a 2nd-tier object. I am not opposed to this if it's a very bland object but if it is a data access component that requires, for example, a connection string, the first tier should not be responsible for that. Apart from that, the more dependencies you have, the more the top tier is responsible for instantiating and passing in all of the dependencies of each object in the slice of the onion it's using.
The only way I've ever seen this problem subverted is through the use of IoC, but I'm working in a situation where that option is off the table. Are there any other ways to structure the code to be testable but not face the problem of instantiating/providing dependencies for each tier in the top tier? I should mention I am working in a web app.
(I've gone over this post as a refresher on "the rules.")
EDIT: I think I can sum up the problem as such: without using some kind of IoC container or bootstrapper, is there a way to structure the code to be testable that doesn't violate the dependency depth principle, which is that every layer in the onion can only reference the layer below it?
It is not about a top tier itself but about bootstraper which will initialize your application. Depending on the rest of the architecture it can either be responsible for launching entry point in top tier of your application or it can simply be part of top tier initialization (that is even used with IoC frameworks).
Example in .NET:
If you are building standalone application you can initialize stuff as part of main execution path and only when everything is initialized you will launch your entry point. In case of web application or web services such bootstraper usually takes place in application start handler and your tiers are used when handing HTTP requests.
Btw. Question should be about IoC container. Not about IoC itself. IoC is the approach to control inner logic from outside - that is achieved by injecting dependencies. It is main approach for easily testable applications. IoC container is part of a framework which builds dependency hierarchies for you.