When you're stuck on how to do TDD for a refactor; and can't find the test to drive the refactor you'd like to see; a spike can often help.
What I've done are a spike w/o tests to figure out a solution and then figure out the tests required to drive the code to that solution.
I did this recently with the Android HackerNews App. During the Clean Architecture change; I didn't see how I could cleanly decouple things in steps. I just did it, w/o tests. I then did a
git reset --hard because I knew where to go; and it was a matter of getting there.
What I realized from that, and it crystalized as I was driving home today - When having trouble seeing how to do the desired refactor; the spike isn't to find the solution and find a way to test to it. (I'm very much ignoring the horrible idea of just writing the tests after)
The spike's job is to let the code show us the smallest test we need to write.
Once you realize how to you can test/change a small thing toward the refactor; reset the code and write that test.
If the next test isn't obvious; spike from here.
The reason I say the result of the spike isn't the solution is mostly two-fold - It wasn't TDD'd; and likely will need work to be cleanly testable.
The other is that the solution you came up with; is likely not what the code will tell you to write.
Similar? Sure. That solution; hehe - Not Likely.
When I did the Clean Architecture change; I got a similar result, but it's far less ugly.
I found a small test and it drove the code through less change and churn than my spike solution would have. I could have driven to the same solution as I had in the spike, but it wouldn't have been a solution the code wanted. It would have caused problems at some point.
I'd like to think that in 10 years I'll have the experience to know how the code will want to evolve things... but I consider people like Fowler, Beck, Fred George... and while I've got an ego...
I'll never be better than test driven code at identifying what the code needs.