You can try loading the assemby for testing with the Assembly.Load(byte[])
method. I'm not sure if your test runner can handle this, but this will give you an assembly to work on that is loaded from a byte stream in memory. Therefore the original assembly file can be recompiled anytime and you can have as many concurrent versions of this assembly loaded as you like. They are all separate, with separate types.
I use a similar strategy with the RevitPythonShell script loadplugin.py for loading plugins at runtime and then exercising them for testing. This seems to work quite well except for WPF controls defined in XAML. I suspect the XAML parser and loader keeps a cache of the types, but haven't been able to look into this yet due to time constraints.