changing access levels for a test: code smell?

39 views
Skip to first unread message

Justin Collum

unread,
Jun 14, 2012, 3:20:32 PM6/14/12
to Portland ALT.NET
I've got a private virtual void method in a class. The method writes to an audit log. I need to get this method under test and make sure that the audit log is getting written whenever there is a failure in a certain method. I haven't fixed it yet, but my first thing to do was to promote that method to a public and add it to the interface. 

I'm looking at it and somehow it seems wrong. Why am I changing the access level of a method so that I can test it? The method was private (or protected) when I wrote it and that is the correct access level. I'm just feeling like there's something inherently broken in the testing story in C#. Do I really have to get a paid tool like TypeMock so that I can avoid this mucking about with existing code so that I can test it? I'm probably going to have to modify three or four classes just so I can make sure that the audit log is written when an exception happens. That's a code smell to me. 

Am I looking at this the wrong way? 

Mark Knell

unread,
Jun 14, 2012, 3:28:31 PM6/14/12
to pdxalt...@googlegroups.com
Consider encapsulating the logging operation into a separate object, one that is injected at construction.

Uncle Bob has a clever formulation of the SRP applies here: a class should have one reason to change. I don't know your domain, but I'd bet that the implementation of the logging (the tools that do it, the location of the logs, their reliability and fidelity, etc.) is not the purpose of your class, much less your interface.

HTH,
Mark

PS  Private virtual? Is that a typo? Surely you meant protected. 

Justin Collum

unread,
Jun 14, 2012, 3:45:39 PM6/14/12
to pdxalt...@googlegroups.com
Right, I meant protected. 

What I'm getting at here is that sometimes making far reaching changes just so I can get one method under test seems like it's a limitation of the language. Ruby doesn't do this stuff -- if you want to change what a class does, you just do it. There's no IOC / DI. That complexity just isn't needed.  http://davybrion.com/blog/2010/10/why-you-dont-need-dependency-injection-in-ruby/ 

Could probably make the contrary argument that Ruby lets you make your class pretty poorly and C# enforces highly organized UML-fancy classes. 

But I'm looking at an existing codebase that's quite large. If I have to add in a logger to the ctor for every place one of these objects is created, I might be in for hours of work. Couple that with poor testing coverage and suddenly it's gone from "a lot of work" to "no way my boss will sign off on that". I feel like this scenario is more the rule than the exception in the .NET world. Is DI even widely practiced in the .NET world at this time? I'd say no. 

I heard a quote recently that strongly typed languages are a bit like a straitjacket: some people feel constrained by them, others think "oh, I'm all cozy in my straitjacket here!" 

Loki

unread,
Jun 14, 2012, 4:08:55 PM6/14/12
to pdxalt...@googlegroups.com
Heres two methods you might find interesting:

The recommend (i.e. Microsoft "please use our test framework") method is here:  http://stackoverflow.com/questions/250692/how-do-you-unit-test-private-methods 

Personally I try and only test contractual members (i.e. public interface methods) and use those tests to verify internal functionality (logging, etc). Subclassing in my test suite as needed to accomplish this.

Also not sure if you have seen Moq:  http://code.google.com/p/moq/  havent used it in a long time, so not sure if it would work for your needs.

- Will

Ryan Eastabrook

unread,
Jun 14, 2012, 4:10:44 PM6/14/12
to pdxalt...@googlegroups.com

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

Loki

unread,
Jun 14, 2012, 4:51:09 PM6/14/12
to pdxalt...@googlegroups.com
Guess I didnt look into it too much. Sorry. This appears to be a working sample:  http://exposedobject.codeplex.com/ but I am not sure how stable it is.

On Thu, Jun 14, 2012 at 1:49 PM, Justin Collum <jco...@gmail.com> wrote:

On Thu, Jun 14, 2012 at 1:08 PM, Loki <grayw...@gmail.com> wrote:

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 Collum

unread,
Jun 14, 2012, 4:59:35 PM6/14/12
to pdxalt...@googlegroups.com
My feeling is that loggers should be universally available -- every class should be able to easily do logging. Unfortunately the codebase that I'm working in doesn't have that. I'd need to change the class to take a logger in its ctor, and as I discussed earlier, that's not feasible here. 

Troy Howard

unread,
Jun 14, 2012, 5:54:33 PM6/14/12
to pdxalt...@googlegroups.com
I agree with the dependency injection method suggested by others...
but keep in mind there's lots of ways to accomplish this effectively
(beyond constructors).

In C#... Writing your code for testability is important. This is why.
In Ruby/Python/etc, sure there are tricks that are cool for this kind
of thing, but we're in C# here, so it really doesn't matter. The
features of the language are valuable for other reasons. If you want
testability, you have to write for it.

One way:

public interface ILogger {
void Log(string msg);
}

public class Logger : ILogger {
public void Log(string msg) {
// do something...
}
}

public class Foo {
public Foo(ILogger logger) {
_logger = logger;
}
private ILogger _logger;
private void Log(string msg) {
_logger.Log(msg);
}
}

Another way:

public class NoopLogger : ILogger {
public void Log(string msg) {
// do nothing...
}
}

public class Foo {
public Foo() {
Logger = new NoopLogger();
}
public ILogger Logger { get; set;}
private void Log(string msg) {
Logger.Log(msg);
}
}

var foo = new Foo();
foo.Logger = new Logger();


Another way:

public class Foo {
public Foo() {
Log = (m) => {};
}
public Func<string> Log;
}

var foo = new Foo();
var logger = new Logger();

foo.Log = logger.Log;
// or
foo.Log = (msg)=> { //do something };


Another option is just using the built-in framework for logging in
.NET (Debug/Trace/etc) and registering listeners that do the logging,
and your test can verify the log action via a listener.

Thanks,
Troy

Adron Hall

unread,
Jun 14, 2012, 8:27:10 PM6/14/12
to pdxalt...@googlegroups.com
What Troy said. 

If you're dealing with a simple app though, I'd do some injection, just roll an abstraction if you have to... whatever the case. 

Also, if a method is private or protected, the other big question is why and why are you needing to test it? Maybe take a behavioral testing (BDD) approach instead. You may find you either don't need the protected or private or whatever it is or you may find that the design is inappropriate in the first place.



-Adron
--
Adron B Hall
Iron Foundry Projecthttp://www.ironfoundry.org

Mark Knell

unread,
Jun 18, 2012, 5:34:14 PM6/18/12
to pdxalt...@googlegroups.com

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

Mark Knell

unread,
Jun 21, 2012, 3:55:35 PM6/21/12
to pdxalt...@googlegroups.com
Justin,

Still another approach to construction-time injection is to introduce a "back door" ctor (constructor) that allows dependencies to be injected, while also keeping a smaller public ctor that defaults to certain values.  This approach  does not require you to update existing callsites, modify the interface, or even promote the visibility of your private method.

So in your case, you might have 

public class Foo: IFoo {
  private readonly ILogger logger;
  public Foo() : this(new Logger()) {}
  internal Foo(ILogger logger) { _logger = logger;  }
  private void Log(object data) { _logger.Log(data); }
}

The backdoor can use whatever polymorphism you prefer. If you dislike interfaces, you could subclass Logger with a test double, or you could go functional and accept an Action<object>, so _logger would be a delegate and the body of Log() would be "_logger(data);".

Note the internal ctor. If you expose internals to your test assemblies, your tests can supply a test double for the logger, while all production code would still use the concrete Logger instance, and the production callsites remain unchanged. One drawback is that any production code that has internals access can also use this new ctor. But, if you like Ruby, I'm guessing you're okay with programming by convention already.

HTH,
Mark

Reply all
Reply to author
Forward
0 new messages