Unit Testing Strategy: Hiding Out In the Open

Sometimes there are pieces of code that you want to test but you just don’t quite see how you can unit test it easily.  I have illustrated one such example below where I am interacting with the file system.

Take the following snippet for example:

   1: public class FileRenamer : IFileRenamer
   2: {
   3:     public void RenameFile(IList<DomainFile> files)
   4:     {
   5:         if (files == null)
   6:             return false;
   7:         
   8:         foreach (var file in files)
   9:         {
  10:             var newName = string.Concat(file.FullFileName, file.Client, file.Site, file.DesagilationCount, ".file");
  11:             File.Copy(file.FullFileName, newName);
  12:         }
  13:     }
  14: }

I want to “unit test” that the correct file is created, but I really can’t.  The “File.Copy()” is going to hang me up a bit because in order to verify the method worked I am going to have to go visually verify that a file was copied to a certain location.  I could of course automate this but in either case I’m touching the file system, which is not a quality of a good unit test.

In reality, the only thing I really care about here is that the new file is named properly.  However, as it sits, the new name is really coupled to the copy process and the hard disk. Let’s change that…

   1: public class FileRenamer : IFileRenamer
   2: {
   3:     public void RenameFile(IList<DomainFile> files)
   4:     {
   5:         if (files == null)
   6:             return false;
   7:  
   8:         foreach (var file in files)
   9:         {
  10:             File.Copy(file.FullFileName, BuildNewFilename(file));
  11:         }
  12:     }
  13:  
  14:     public string BuildNewFilename(DomainFile file)
  15:     {
  16:         return string.Concat(file.FullFileName,file.Client,file.Site,file.DesagilationCount,".file");
  17:     }
  18: }

 

What I’ve done here is move the logic of the new name creation to a new method.  By extracting that single line to its own method, we’ve made the “untestable”, testable.

So at this point you may be asking yourself why this strategy is called “Hiding Out In the Open”? I call it that (is there another more formal name? possibly an xUnit test pattern name?) because we haven’t added the method to the interface (the “hiding” part) but we have made the method public so it’s visible to consumers (the “out in the open”).

Hiding

When I added the public method to the FileRenamer class, I *did not* add it to the interface IFileRenamer.  That means anywhere where this particular class is referenced as an IFileRenamer that the new method (BuildNewFilename) won’t be visible (see below). Hence the usage of the term “hiding”:

image 

Out In the Open

The whole point of this exercise was to gain some testability aspects for this particular class.  Since the method is public, in our test method when we create an instance of our FileRenamer class, we now have the ability to test (see below):

image

 

Some Caveats

Some will argue that the renaming of the file is a separate concern from the copy.  The separate concern would then be a separate class that is possibly used by the FileRenamer, in which case the new class would be testable.  This is a fair argument and at times it’s a good strategy.  I think it’s good to know the strategy so there are more tools in the bag when a curious situation arises.

Some will argue that you’re increasing the surface area of your API when you make the method public.  Again, a fair argument however, in most cases when dealing with the abstraction (the interface) this doesn’t rear it’s head.  It’s a small insignificant side-effect.  If you don’t like it don’t use it.  Disclaimer: I strongly discourage this course of action if you’re building a public API that others will consume.  In my case, 99% of software I write is used in house. If making something “public” really bothers you, then make it internal and use the “InternalsVisibleTo” assembly attribute to give your test assembly visibility.

Conclusion:

The original snippet of code above had some pieces that were hard to properly unit test.  What we’ve done here employed a strategy I call “hiding out in the open” to the class under test to achieve a bit more testability. I think this is just another testing strategy to have in your back pocket.  There should be few times when you need this particular strategy, but when you need it it does come in handy.


Posted 12-16-2009 9:38 AM by Tim Barcz

[Advertisement]

Comments

Mark Nijhof wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-16-2009 11:20 AM

Nice indeed, yeah not everything needs to be exposed in an interface. One thing I like as well (what I tweeted about) is to use interface explicit implementations that way the method or property is even hidden when accessing the class directly, and only available when casted to the specific interface. I don't nessicarly use this for testing but to hide things from the user mostly plumbing.

Harry McIntyre wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-16-2009 11:33 AM

A not totally dis-similar trick I like to use is to extract the  untestable code (in this case, File.Copy) into a protected method. Then instead of writing tests against the FileRenamer, I test against a Mock<FileRenamer> with CallBase true (or class TestableFileRenamer : FileRenamer and override the FileCopy method).

Tim Barcz wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-16-2009 11:39 AM

@Harry,

Good idea. Another similar method to yours is a test specific subclass, where you create a class which overrides the extracted method and does nothing, it's basically a hand-rolled fake at that point.

Thanks for the feedback.

Chris Camal wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-16-2009 11:57 AM

I prefer test specific sub classes than the above.  It kind of feels like testing specific code bleeding into production code.  More of a personal thing really :)

Tom Opgenorth wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-16-2009 12:03 PM

Another technique is similar to Harry's:  extract the untestable code into a protected internal method.  The use the InternalsVisibleToAttribute so that your test project can see the extracted method.  Consumers of your code can't.

Plus, if you use Rhino.Mocks you can do a PartialMock of your class and stub out that method while you're testing FileRenamer.

Mark Nijhof wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-16-2009 12:26 PM

Ah and the technique stolen from Ayende but I like to use these constructs as well for system stuff that I don't feel like injecting:

   public static class SystemDateTime

   {

       public static Func<DateTime> Now = () => DateTime.Now;

       public static void Reset()

       {

           Now = () => DateTime.Now;

       }

   }

Before you start the test just replace the Func implementation and afterwards call Reset and in the code you use SystemDateTime.Now() instead of System.DateTime.Now

-Mark

Andriy Buday wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-16-2009 1:12 PM

Sweet, now I will know how to call one more thing that I do sometimes for Unit Testing. But I like using Mocking Frameworks like Rhino. One interesting thing here is if you want to mock concrete class you still need public/protected and virtual.

So anyway you need to be more opened to the rest of world if you are doing Unit Testing.

uberVU - social comments wrote Social comments and analytics for this post
on 12-16-2009 4:37 PM

This post was mentioned on Twitter by TimBarcz: Blogged: Unit Testing Strategy "Hiding Out In the Open" - A strategy I use to get testing in some hard to reach areas. http://bit.ly/5IS1X3

James wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-16-2009 10:31 PM

Tangentially, we've adopted the approach of abstracting all file system operations behind an IFileSystem interface.

Highly, highly recommended.

You have no idea how much more faster and predictable it makes unit tests when you don't have to worry about setup/cleanup, having a complete in-memory implementation, and all our "fiddly" code now has full batteries of tests.

Sanjeev Agarwal wrote Daily tech links for .net and related technologies - December 16-18, 2009
on 12-17-2009 7:08 AM

Daily tech links for .net and related technologies - December 16-18, 2009 Web Development Best practice

Mel Grubb wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-17-2009 12:01 PM

I've gone so far as to make such methods private, and then used reflection to invoke them for testing. You can use Visual Studio generated accessor classes, or some simple extension methods (melgrubb.spaces.live.com/.../cns!A44BB98A805C8996!235.entry?wa=wsignin1.0&sa=190544721) to do the same thing.

Mel Grubb wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-17-2009 12:08 PM

Hmmm, the URL got destroyed. Here's a TinuURL version of the same thing:

http://tinyurl.com/ylxubg3

Rob Reynolds wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-18-2009 4:36 AM

I do the same thing as James. code.google.com/.../WindowsFileSystemAccess.cs

Jarin wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-18-2009 12:33 PM

Your approach doesn't seem to be very SOLID. Your class is doing two things... 1) It's copying files, 2) It's building a filename. What about separating those two resposibilities? You can either create new interface (for example IRenamingStrategy) with method like "string GetFilename(DomainFile file)" and inject the strategy through FileRenamer's constructor using IoC container. Alternatively, you can extend your RenameFile method with a delegate, like Func<DomainFile, string> to "plug-in" your renaming strategy. There are probably some other ways of doing it. One way or another by externalizing the renaming task you can easily create unit test for it. Btw, you should still test that the file gets coppied to the right location but that should be part of automated integration test.

John Sonmez wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-18-2009 1:51 PM

I have ran into this problem several time in the past and have thought about some different ways to solve it.  I think I have probably tried every way from, actually mocking the real file system class, to creating a file system interface, to actually using the file system.  I am of the opinion that the best solution in this situation is to go ahead and create test file in your unit tests and actually interact with real "test file" data.  At the same time though I extract the file system parts into it's own class so that there is no logic aside from real file system logic.  The reason I have arrived at this conclusion for solving the problem is two-fold.  1. Mocking out the file system is difficult and seems to be a waste of time.  2.  When you mock out the file system you are still not testing real functionality of copying a file or opening a file, which actually has some logic.  If you open and read a file, you need to make sure that will actually work.

Mark Needham wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-20-2009 6:07 AM

That's a pretty neat idea actually, hadn't thought of that.

One other way which I'm sure you know is to create an interface for File and then use the real file in prod code and a fake for test code. Bit more verbose perhaps.

I wrote about it a while ago - www.markhneedham.com/.../testing-file-system-operations

Robert Lewis wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-20-2009 11:05 AM

Notice that BuildNewFilename() doesn't depend on any instance data and doesn't have any side effects. It describes a simple algorithm which is very easy to test directly. Because it has no side effects, there's not much danger in making it public or internal. If the algorithm needs to be configurable, it probably belongs in its own class. If it varies according to subclass, it can be a virtual instance method. But for this simple case I would make it public (or internal) and static and test it directly. I agree that the actual file copy operation should be tested in an automated integration test as well.

Tim Barcz wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-21-2009 8:16 AM

I have and would consider creating a IFileSystem abstraction (actually have one in a project I work on), however that may not always be the most preferred route (for a number of reasons, the main one being the understanding of the team on which you work).

It's like a carpenter who has many tools for the job.  The expertise and seasons of experience will tell him which to use and when.

Jarin wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-21-2009 11:45 AM

Robert, I can see your point and for this particular, simple case, making it public is good enough. I was just trying to give an alternative, more generic solution.

It is interesting, that because, as you say: "BuildNewFilename() doesn't depend on any instance data and doesn't have any side effects", you would keep it in the class. For me, if some method doesn't use instance data, it is clear indication of different responsibility and I'd put it in different class or at least make it static for trivial cases like this one.

Richard Dingwall wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-23-2009 2:32 AM

I've used this pattern as well, particularly for testing code inside loops.

I justified that making private methods public for testing (and not formally adding them to the interface) is okay, because you're testing the implementation behind an interface, not the interface itself; none of your production code knows it exists.

Also I think it's much simpler than using subclasses or InternalsVisibleTo.

Richard Dingwall wrote re: Unit Testing Strategy: Hiding Out In the Open
on 12-23-2009 2:34 AM

@Mark Nijhof : Ayende's testing pattern has a name btw! Michael Feathers calls it Supercede Getter in WEWLC.

Latrice wrote re: Unit Testing Strategy: Hiding Out In the Open
on 10-10-2011 2:48 AM

This “free sharing” of infomartion seems too good to be true. Like communism.

social bookmarks wrote re: Unit Testing Strategy: Hiding Out In the Open
on 01-18-2013 12:41 AM

s5PQlO Great post.Really thank you! Keep writing.

lose weight pills wrote re: Unit Testing Strategy: Hiding Out In the Open
on 02-02-2013 12:42 AM

xs8On3 I cannot thank you enough for the post.Really thank you! Great.

buy discount viagra wrote re: Unit Testing Strategy: Hiding Out In the Open
on 02-03-2013 6:25 AM

zTQOLt This is one awesome article.Much thanks again. Will read on...

buy stendra online wrote re: Unit Testing Strategy: Hiding Out In the Open
on 02-15-2013 6:31 PM

oL7MCV Great article post.Thanks Again. Cool.

buy cialis discount wrote re: Unit Testing Strategy: Hiding Out In the Open
on 02-23-2013 3:57 PM

SqwQAt Thank you ever so for you article. Awesome.

buy clomid wrote re: Unit Testing Strategy: Hiding Out In the Open
on 02-28-2013 11:53 PM

1XIPZS Enjoyed every bit of your post.Really thank you! Much obliged.

bookmarks wrote re: Unit Testing Strategy: Hiding Out In the Open
on 03-13-2013 8:02 PM

ZryAUj Major thanks for the article post.Really looking forward to read more. Awesome.

social bookmarking service wrote re: Unit Testing Strategy: Hiding Out In the Open
on 03-23-2013 2:55 AM

SXzGta Thank you for your article post.Much thanks again.

comedy wrote re: Unit Testing Strategy: Hiding Out In the Open
on 04-06-2013 9:45 AM

I loved your post. Cool.

comedy wrote re: Unit Testing Strategy: Hiding Out In the Open
on 04-06-2013 6:31 PM

Wow, great article.Really looking forward to read more. Cool.

digital camera guide wrote re: Unit Testing Strategy: Hiding Out In the Open
on 05-14-2013 1:30 AM

uKMsVT Major thankies for the blog article.Really looking forward to read more. Awesome.

cheap social bookmarks wrote re: Unit Testing Strategy: Hiding Out In the Open
on 06-19-2013 3:24 PM

RqzQ35 Thank you for your post.Much thanks again. Will read on...

click here wrote re: Unit Testing Strategy: Hiding Out In the Open
on 07-23-2013 1:07 PM

Major thankies for the blog article.Thanks Again. Awesome.

buy cialis online cheap wrote re: Unit Testing Strategy: Hiding Out In the Open
on 07-24-2013 2:41 PM

I really liked your article. Really Cool.

news wrote re: Unit Testing Strategy: Hiding Out In the Open
on 07-26-2013 6:41 AM

pN7iG3 Enjoyed every bit of your blog.Much thanks again. Will read on...

best news wrote re: Unit Testing Strategy: Hiding Out In the Open
on 08-02-2013 11:17 AM

bwFbra Im grateful for the article.Thanks Again.

news news news news wrote re: Unit Testing Strategy: Hiding Out In the Open
on 08-04-2013 11:59 PM

ruX0jE Thanks for sharing, this is a fantastic blog article.Thanks Again. Keep writing.

great link buildng wrote re: Unit Testing Strategy: Hiding Out In the Open
on 08-19-2013 8:30 AM

650bjl I cannot thank you enough for the post.Much thanks again. Great.

great seo service wrote re: Unit Testing Strategy: Hiding Out In the Open
on 09-03-2013 9:43 PM

8fhxpY Really informative article.Really thank you! Fantastic.

awesome link building wrote re: Unit Testing Strategy: Hiding Out In the Open
on 09-24-2013 4:13 PM

XQ6EDV I really liked your blog article.Much thanks again. Really Cool.

link building team wrote re: Unit Testing Strategy: Hiding Out In the Open
on 10-01-2013 1:15 AM

DCVSAv Really appreciate you sharing this blog. Really Cool.

link building team wrote re: Unit Testing Strategy: Hiding Out In the Open
on 10-16-2013 12:10 PM

T5biwx Im obliged for the post.Really thank you! Really Cool.

take a look at it! wrote re: Unit Testing Strategy: Hiding Out In the Open
on 10-25-2013 12:38 PM

Cgxflf Thanks so much for the post. Really Cool.

link building wrote re: Unit Testing Strategy: Hiding Out In the Open
on 11-02-2013 7:11 AM

HC0NXq I cannot thank you enough for the article post.Really thank you! Will read on...

matzcrorkz wrote re: Unit Testing Strategy: Hiding Out In the Open
on 08-06-2014 2:16 PM

xmomup I am so grateful for your blog. Much obliged.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)