Testing unfriendly code
Say I have the following code, which cannot be modified:
class A {
public doSomething() {
// lots of business logic
data = obtainData();
// lots of other business logic which depends on data
}
private obtainData() {
// connect to the database
// send web requests
// send manned missions to the moon
// process the obtained data
return data;
}
}
What are the best practices of testing such a code? I want to make sure doSomething()
does what it should do, but I want to provide it with know开发者_开发百科n data instead of running the code inside obtainData()
.
Have a look at Moles to help you with brownfield testing. Moles in C# allow you provide your own implementations for private code and other people's code.
When you have a good set of tests that cover this mess, please re-factor to save humanity.
If the originator of the spaghetti code is within reach, please smack him for me, why don't ya?
For best practices, read Uncle Bob's Clean Code http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882
To summarize: if code under test has external dependencies, make them explicit and externally-configurable.
You didn't post the offending code, usually the culprits are new object creation in constructer, static calls, use of constants, and a few others.
The idea is to decouple your class from it's dependencies. The common way of doing this is by dependency-injection.
Also consider having smaller classes that do very specific tasks, chance are if you describe your class task with an "and" in the sentence, it does too much and will be harder to test.
See this post and all related links on it for information relative to testing static and hard-coded dependencies.
Work on the design
- Identify the responsibilities of this class.
- Next, Extract the dependencies of this class. Move all methods that do not align with bullet#1 into a dependency.
e.g. if this class is not responsible for handling databases or network IO, extract them as dependencies of this class. Inject them as ctor args or method args(if only a single public method needs it).
public A(DataRepository repository, WebService service, SpaceStation spaceStation)
{ // cache them as internal fields;}
Now it is not necessary to stub out or subclass and override or increase member visibility for testing.
Your unit tests would create an instance as
_testSubject = new A(new Mock<DataRepository>.object, new Mock<WebService>.object, new Mock<SpaceStation>.object);
whereas your production code would use real implementations of the above roles.
Have you had a look at Mocking? It might be provided by your unit testing framework.
精彩评论