TDD does not necessarily lead to good design. It's possible and really easy to get poorly designed program using TDD.
TDD is just a tool to help us design faster using refactoring, it will never make the design of the program appear magically. TDD is a design help tool. The quality of the design you will get out of TDD depend largely on the capacity of the developer to use refactoring to Design Patterns, or refactoring to SOLID principles.
The developer will make the design emerge using continuous refactoring. It's the most important aspect of TDD: Refactoring.
Applying TDD without doing constant refactoring will often lead to really poorly designed systems which is worst than applying BDUF.
TDD is often associated with the notion of "emergent design". In agile, you often build your software incrementaly, feature by feature. So you can't know right from the start what architecture you will need, it will evolves/emerge with time. So any time you add a new piece of functionality you do some refactoring to improve the design of your application. It's continuous/incremental design. That's why TDD is key in a agile processes.
BDUF is not incompatible with TDD. There is nothing wrong with starting a piece of sofware while having the design already in mind. TDD will then enable you to put that design in place quickly. And in the case the design you thought about was wrong, TDD will allow you to refactor it nicely and safely. Again, it's just a tool, it's there to help us develop our ideas faster and design stuff safely and faster.
So you can either do BDUF+TDD or Emergent Design+TDD, the later is the more common in the agile community because of the iterative way of working.
In all cases you should never try to do emergent design without being willing to do some constant refactoring, they both go together and It does really requires a lot of discipline. Things can quickly spin out of control if you keep adding new features without applying Refactoring.
Refactoring apply to both production code and test code.