The "write test + refactor till pass"
approach looks incredibly
You seem to have a misconception about both refactoring and TDD.
Code refactoring is the process of
changing a computer program's source
code without modifying its external
functional behavior in order to
improve some of the nonfunctional
attributes of the software.
Thus you cannot refactor code until it passes.
And TDD, specifically unit testing (which I consider the core improvement, since other test seem rather plausible to me), is not about redesigning a component until it works. It is about designing a component and working on the implementation until the component works as designed.
Also it is important to really grasp, that unit testing is about testing units. Due to the tendency to always write a lot of things from scratch, it is important to test such units. A civil engineer already knows the specs of the units he uses (the different materials) and can expect them to work. These are two things that often don't apply to software engineers, and it very pro-engineering to test the units before using them, because it means using tested, high-quality components.
If a civil engineer had the idea to use some new fibre tissue for making a roof to cover a
stadium, you would expect him to test it as a unit, i.e. define the needed specs (e.g. weight, permeability, stability, etc.) and thereafter test and refine it until it meets them.
That is why TDD works. Because if you build software of tested units, chances are much better it works, when you plug them together and if it doesn't you can expect the problem to be in your glue code, assuming your tests have good coverage.
Refactoring means: no change in functionality. One point of writing unit test is to ensure, that refactoring doesn't break the code. So TDD is meant to assure, that refactoring doesn't have side effects.
The granularity is not a subject of perspective, because as I said, unit tests test units and not systems, whereby the granularity is exactly defined.
TDD encourages good architecture. It requires you to define and implement specifications for all of your units, forcing you to design them ahead of implementation, which is quite the contrary of what you seem to think. TDD dictates the creation of units, that can be tested individually and are thus completely decoupled.
TDD doesn't mean I throw a software test at spaghetti-code and stirr the pasta until it passes.
In contradistinction to civil engineering, in software engineering a project usually constantly evolves. In civil engineering, you have the requirement to build a bridge in position A, that can carry x tons and is wide enough for n vehicles per hour.
In software engineering, the customer can basically decide at any point (possibly after completion), he wants a doubledeck bridge, and that he wants it connected with the nearest motorway, and that he would like it to be a lifting bridge, because his company recently started to use sailing ships.
Software engineers are tasked to change designs. Not because their designs are flawed, but because that is the modus operandi. If software is well engineered, it can be redesigned at high level, without having to rewrite all low level components.
TDD is about building software with individually tested, highly decoupled components. Well executed, it will help you to respond to changes in requirements signifficantly quicker and safer, than without.
TDD adds requirements to the development process, but it doesn't prohibit any other methods of quality assurance. Granted, TDD doesn't provide the same security as formal verification, but then again, formal verification is extremely costy and impossible to use on a system level. And still, if you wanted to, you could combine both.
TDD also encompasses tests other than unit tests, that are performed at system level. I find these easy to explain but difficult to execute and hard to measure. Also, they are quite plausible. While I absolutely see their necessity, I do not really value them as ideas.
In the end, no tool actually solves a problem. Tools only make solving a problem easier. You can ask: How will a chisel help me with great architecture? Well if you plan to do straight walls, straight bricks are of help. And yes, granted, if you give that tool to an idiot, he'll probably slam it through his foot eventually, but that's not the chisel's fault, as much as it is not a flaw of TDD that it gives false security to novices, who do not write good tests.
So at the bottom line, one can say TDD works much better than no TDD.