It's a poor excuse, in my opinion. We have the choice to treat software the same way that people treat hardware products. Give notice that you will not be supported if you do things that are outside intended use. With appliances, this is called 'voiding the warranty.'
The problem is that we have not developed a culture which accepts those sorts of agreements yet. The closest I've seen (and I've been tracking the testability issue for frameworks and libraries for years) is the Eclipse notion of soft-final.
They used comments to indicate that you should not subclass particular classes. They could have marked them final but they chose not to in order to support their internal development. But, the message was clear: "if you subclass this, and a future version breaks you, it is your problem not ours."
The fact of the matter is that language-based protection mechanisms are too coarse. They are an attempt to solve a social problem (what people are allowed to do) with technology and there's no way to get it right consistently. Better to leave it in the social sphere.
Marking variables as private using naming conventions like foo._dont_mess_with_me is also often helpful. Tools like linters can warn you that you're doing the wrong thing (messing with object internals) but the language doesn't get in your way or force you to use things like reflection to do what you want.
public bool StringIsEvenLength(string somestring )
public string FetchRemoteString( string uri )
public bool RemoteStringIsEvenLength(string uri)
{
fetched = FetchRemoteString( uri );
if( fetched != null ) {
return StringIsEvenLength( fetched );
}
throw new SomeOtherException("Didn't get a string to check the length of.");
}
Now StringIsEvenLength is trivial to test.
When you want to test RemoteStringIsEvenLengthYou can mock out FetchRemoteString and don't even have to use a WebClient at all.
The general point still stands though - if there was an interface, we wouldn't have to come up with these workarounds.
Dependency Injection greatly improves the landscape. Don't instantiate concrete classes when you can have a DI framework construct those objects for you. This will allow you as the tester to supply the mocked objects rather than taking the wrapper approach. It is better if as a team you've introduced this design decision up front, but you can introduce it later for problematic classes that need extra attention. In Java, I love using Guice for this sort of thing, in .NET I've used Ninject in the past.
One thing that .NET has in its favor that Java doesn't is partial classes with an internal access modifier. You can then put your test code in these partial classes and grant them access to your assembly. This allows you to separate your private test code hooks from your shipping code, and it prevents someone from deriving from your class and using those test interfaces incorrectly.
This line is the problem: public WebClient client = new WebClient(); client shouldn't be public and it shouldn't be using a concrete class. Introduce an interface, use DI, and use internal partial classes as needed. If your developer won't do this, find a new developer. Anyone can write code, but writing good testable code with a clean separation of intents is something some developers never learn, usually because no one has told them, "You're doing it wrong."