Does your class need to be tied to your logging object? Dependency injection would be great here, and then of course, you could inject a test logger when it’s under test.
Ryan
On Thu, Jun 14, 2012 at 1:08 PM, Loki <grayw...@gmail.com> wrote:
Heres two methods you might find interesting:
I thought this looked great but then I realized that it is apparently fictional. "AsTransparentObject" can't be found on MSDN and only has 3 or 4 hits on google:
Justin, I'm thinking the root of your difficulty is sloppiness in the code you've inherited, rather than the language or tools. It sounds deeply coupled. I don't think any language can defend against widespread antipatterns. Or, to view it another way: a good carpenter doesn't blame her tools, but she can blame the carpenter that did sloppy work the week before she started.
Specifically, if you have most of your code acquiring objects via constructors, then you have a refactoring headache, whether in C# or Ruby. One notable difference is, the refactoring tools are pretty good in C#--maybe the best of any mainstream language. I'm a couple years out of touch with Ruby, but isn't its refactoring technology pretty much just "regex, retest, and update the wiki"?
To me, the distinguishing characteristic of C# is not so much its straightjackets and whether they are comfy, but the tools that can reason accurately about your code, such that they can refactor a hundred callsites correctly in 0.2 seconds. You could refactor that fast in Ruby, but only trial-and-error (or its robot cousin, your tests) will tell you what was correct.
As for Davy Brion's post, I'm an avid reader of his, but he's a little shaky with this one. Sure, you can avoid DI by simply altering the code under test... but then what are you testing, exactly? Not your production code, that's for sure. As tests get complicated, it's statistically more likely you'll make inaccurate substitutions, such that your tests pass by side effect of the changes you made, rather than because the actual production behavior would have passed the test. Yes, this is a risk with any kind of stubbing/faking/mocking, but there's a difference between changing the class's collaborators and changing the class itself. Here's another point. If you know that a particularly convoluted part of your class can be smudged out of existence in the unit tests, it's easier to live with. By insisting that the class-under-test is identical to the production code, you get both higher fidelity AND design pressure toward cleaner code.
(There's some great stuff in Ruby, don't get me wrong. I'd love to have mixins in particular, and better C# conventions around the use of dynamic language features such as Rubyists enjoy. And RoR was game-changing.)
Davy never goes too far wrong; he does point out that one of the benefits of DI is to reduce coupling, which brings me back to my original point: you don't have a language problem, you've got a coupling problem.
One last thing. If you really love Davy's approach, it's doable in C#. Not all DI has to be done through the constructors. Note that I'd said "at construction", which is not the same thing as "in the constructor". Some engines let you decorate properties (certainly public and protected, and I think even private ones) that will be supplied at construction-time. This form of DI doesn't require any change to the constructor or the interface. Now consider that the property doesn't have to be data; it can be a delegate to a method, aka behavior. In fact, it can allow basically the same thing that Davy's talking about: to remove concrete dependencies, add a step of indirection (in his post, this was the "get_dependency" method) by calling a delegate rather than the thing itself, then alter that delegate at test time.
Others on this thread have pointed out additional techniques, such as test-only subclasses that override the production behavior. RhinoMocks and others free frameworks will let you create "partial mocks" dynamically, where you choose which members are replaced (if they're virtual--sometimes a big "if"). If you want a pretty thorough catalog of common xUnit testing patterns, try Gerald Meszaros' "xUnit Test Patterns: Refactoring Test Code". It's five years old, so there's a few things now you can do with C# that you couldn't back then, and at $50 it's not inexpensive, but AFAIK it's still the biggest and most complete resource on the subject, with many ideas that are generalizable well beyond xUnit languages and tests.
HTH,
Mark