I'll teach you anything you want to know, just don't ask me any questions.

Month: January 2014

Lets recap a bit from last time, even though you could just go back and re-read it. I’ll sum up. You want to reduce side-effects where possible.

Benefits of Limiting Side-Effects:

out-of-order / parallel test execution

There’s nothing worse than having the order of your tests matter

no tear-down

if you have real work going on in your test teardown you’re doing it wrong.

less likely for other code to depend on those side-effects

This is subtle, but if you leave side effects hanging around, people might take them for granted and assume that they will always be there. This is important because if you later decide to change your code and remove the side-effects, you may break people.

Benefits of relying on abstractions:

Modular code design has many benefits but for testing the most obvious benefit is being able to use a test double in place of the real concrete implementation. This double allows for your tests to cleanly operate on just the class or method being tested, (the term SUT is often used to describe this) Leave yourself Test-Seams, even if you don’t use them right away. You don’t have to go full Dependency Injection with a framework like Ninject or Funq to reap the benefits of DI. Manual dependency injection works quite well in most cases where you only need to inject either a real concrete class or a test-double. The default for the property or constructor being injected can be the normal dependency for your production code, but leave yourself an area to stitch in the dependency later to isolate your class for the test. For example: (this is totally contrived, sorry, I work in finance.)

The default parameterless constructor handles your default dependency registration, but you’ve left yourself open to inject a dependency later for testing when you need it. You can mark this as internal and expose internals only to your UnitTest project using the InternalsVisibleToAttribute for extra assurance that this method will not be called by normal production code. You could also apply the Conditional attribute and #define a UNIT_TEST preprocessor symbol but that’s getting a bit paranoid. This works pretty well for single dependencies in the ideal case, but if you have more than one you might want to switch over to property injection instead for convenience. The hard-line unit tester in me wants to keep things as dogmatically strict as possible, but it’s not always convenient to follow the rules. Provided you aren’t using the other default dependencies in your tests by accident, injecting one or two dependencies into a class which depends on lots of things through properties seems reasonable. You certainly don’t want to run into this:

public class GodObject
{
public GodObject()
: this(new DatabaseLayer(), new ServiceController(), new LoggingModule(), new WidgetFactory, new OkayYouGetWhereIAmGoingWithThis();
}

That thing is far too dependent on other classes to accomplish the desired task, but if this is a brownfield project and you are in the process of backfilling tests this might be a reasonable approach. At the very least the setup constructor will be a constant reminder that you badly need to refactor that thing as soon as possible.

Okay, that’s all the good advice I can muster right now. Keep on testing and check back soon for more smug than you can handle. Tune in next time for “10 reasons you should stop using LINQ. Seriously. Stop it.”

It is a well-established concept that the best way to get 100% code coverage is to write your unit-tests from the very beginning of a project, such that each test influences the code, and the code immediately influences the tests as it evolves. This is more than just a strictly dogmatic approach to code construction reserved for ultra-ninjas and people who hire dominatrices (I’ve never had to pluralize that word before, I swear) to dig their heels into their backs for pleasure, this is good advice. Test-driven development is the best way to get solid code coverage, but for reasons often beyond our control we don’t have the luxury of TDD. Either because we’ve inherited a brownfield project with lots of legacy code, or because our deadlines are tight and we just don’t have time to convince our pointy-haired bosses that having tests provide business value. Or we’re just lazy.

This puts us in a bit of a bind. As good developers we know that having good code coverage is a good thing. It’s like flossing, or eating our vegetables; we know we should do it, we know it’s good for us and will prevent a whole host of misfortunes later on, but we don’t because we think it’s a chore. We might write a few tests later, when we have time, or when we need to debug something, but by then the code has morphed into untestable spaghetti. All hope of an isolated repeatable unit-test has gone out the window, and the best we can hope for is something that requires a lot of setup, and database, and possibly some services, and a bit of luck to work once, followed by more code to tear-down all the changes the test has made. Sound familiar?

So, you’re not into TDD, but you don’t want writing tests to feel like agony. Luckily, there is a middle ground. Write your code as if you plan to test it later. Design your code in such a way that testing it should be easy. If you follow a few fundamental rules you will find that in addition to being protected from regression bugs when you write your tests, your code will get measurably better and contain fewer bugs simply by virtue of the WAY you are constructing it.

1) Limit side-effects.

Okay, if you’ve ever listened to any academics talk about functional programming you are probably sick to death of hearing about immutability and side-effect free code, and I’m not advocating exactly that, but simply asking you to think about how your code will be used with respect to state. Does your code behave the same way the first time as it does the third? This concept touches on a function’s purity, in the mathematical sense. Put simply, if a function is pure then testing it is easy. If a function uses lots of state and is highly coupled with other classes, then testing becomes that dreaded chore we are hoping to avoid. Set yourself up for success and keep your classes and methods small, uniquely responsible for its’ functionality, and have fewer dependencies (both in terms of state and additional classes it needs to accomplish its’ job), and you may find that your code is not only more testable, but also easier to understand and more resilient to change. Isn’t that nice?

2) Depend on abstractions.

Your eyes just rolled back thinking about the D in S.O.L.I.D. right? Yeah, well, I’m not sorry. It’s good advice if you apply it correctly. I’ll use the canonical example of LINQ which I assume everyone in the .Net world has used by now (and should probably stop abusing). The power and flexibility of those query methods is directly related to a single elegant abstraction ( IEnumerable<T> ). Each method in that stack only needs to know how to enumerate some collection to be effective. These methods work on linked-lists, arrays, sets, even some crazy infinite “virtual collections” that materialize data only when the next item is requested.

In the same way, your own code can be flexible with respect to the arguments used to call it. Generalize! Does your method really require a concrete instance of Collections.Generic.List<T>? Are you adding and removing elements? If so, maybe try IList. Need just the Count property? Downgrade your required parameters to take an ICollection. It doesn’t even need that!? Your method just uses foreach over the collection of things and does something for each thing. Have I mentioned how useful IEnumerable is?

Your code is now much more useful, and also provides some added context about how it will use the parameter to the callers. IEnumerable gives them a nice guarantee that you (probably) won’t do something sneaky like adding or removing items from their collection. Obviously if you were malicious you could try casting it to some other interface that supported that behavior, and then tamper with the collection, but those are the sorts of ill-willed malcontents who would cast off their const-iness in C++ and we should just try to avoid them and hope for the best.

Stay tuned until next week when we talk more about writing testable code with some practical examples. Same smug time, same smug channel.

Disclaimer

The intensely smug opinions expressed herein most definitely do not represent those of my employer.