What we do:
We have separate solutions, subversion repositories, and builds for each layer.
Each layer has dependency builds so that building a lower layer will cause that layer to build.
Each layer has a Lib directory with externals pointing at the lower layer output directories.
Prior to a build, any layer(other than the root one), will do an update on the external folder(s), pulling the latest binaries in. If the build is successful, then it will update the external link to latest version, and then check in it's own binaries. This will trigger the next build down the chain, etc.
This way a breaking change in a lower level layer will fail builds up the chain, but if you do a get from the upper level layer it will still work in the most recent functional check-in.
As for deciding on layers, we pretty much have a framework layer, business logic layers, and presentation layers(either web service, application, or web site).
As for tests, they should be in the same solution as what they are testing, but different assemblies. The test assemblies should never be deployed to a production environment. The reason is that often, if not always, you will have your test assemblies be "friend" assemblies to the real ones. Assuming an attacker can execute any public members on deployed assemblies this gives them access to not only public members on your real code, but also internal ones.
Also, we keep things related to building, tools, config files, etc, in their own repository, separate from the code.