public
Created

Rebuttal to C# Shims

  • Download Gist
gistfile1.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
//presented class (from http://www.peterprovost.org/blog/2012/04/25/visual-studio-11-fakes-part-2)
 
namespace ShimsDemo.SystemUnderTest
{
public class CustomerViewModel : ViewModelBase
{
private Customer customer;
private readonly ICustomerRepository repository;
 
public CustomerViewModel(Customer customer, ICustomerRepository repository)
{
this.customer = customer;
this.repository = repository;
}
 
public string Name
{
get { return customer.Name; }
set
{
customer.Name = value;
RaisePropertyChanged("Name");
}
}
 
public void Save()
{
customer.LastUpdated = DateTime.Now; // HOW DO WE TEST THIS?
customer = repository.SaveOrUpdate(customer);
}
}
}
 
//Dead easy way...refactor Save to create a seam:
public void Save()
{
customer.LastUpdated = CurrentDate();
customer = repository.SaveOrUpdate(customer)
}
 
//Now the test is:
 
public ShimClassUnderTest: ClassUnderTest
{
public override CurrentDate()
{
return OurTestTime;
}
}
 
 
//using tdd would have driven that out, either because we would have refactored,
//or we could have done something like:
 
ClassUnderTest.ShouldReceive(:CurrentDate).AndReturn(OurTestDate);
 
//of course, I'm not up to speed on the latest C# frameworks like that, but you get the gist.
//Ha, ha. Gist. Get it? <sigh>
 
//Second example:
public void StartTimer()
{
var timer = new DispatcherTimer();
var count = 30;
timer.Tick += (s, e) =>
{
if (count == 0)
{
count = 30;
RefreshData();
}
};
timer.Interval = new TimeSpan(0, 0, 1);
timer.Start();
}
 
//The bigger question *isn't* how do we test this. It's how did we get here?
//This is godawful code. Let's try refactoring
 
public void StartTimer()
{
var timer = new DispatcherTimer();
var count = 30;
timer.Tick += (s, e) =>
{
if (count == 0)
{
count = 30;
RefreshData();
}
};
timer.Interval = new TimeSpan(0, 0, 1);
timer.Start();
}
 
//again, my C#-foo is weak, but
 
void TimerDelegate(Object sender, EventArgs e) //IIRC
{
if(count == 0)
{
count = 30;
RefreshData();
}
}
 
public void StartTimer()
{
var timer = new DispatcherTimer();
var count = 30;
timer.Tick += TimerDelegate;
timer.Interval = new TimeSpan(0, 0, 1);
timer.Start();
}
 
//Now, to start, we don't need to actually test a timer fires every second,
//because our method doesn't care about that behavior, so our tests for it shouldn't
//either. Oh, if we *wrote* our own timer, then tests should be there, but not here
//We should have tests to make sure the *behavior* is correct. Using state-based:
 
public void TimerShouldBeInitializedCorrectlyTest()
{
classUnderTest.StartTimer();
AssertEqual(timer.Interval, new TimeSpan(0,0,1));
}
 
//What's that? Timer isn't public? That's a smell, because we want to test it. so
//either make it protected, or figure out what the abstraction is really trying to tell you
 
//Again, the shims article has good advice in it. But failing to point out that if you
//write tests before you write code, and you do some prefactoring before writing, you'll
//end up in a much better situation.
 
//IMHO

Of course this is a good refactoring step, and you would really like to go further with is and introduce something like a date provider service that can be included through IOC of some kind. But to get from the original code to the first refactoring required you to change the implementation WITHOUT supporting unit tests. Shims can get you out of that trap, then after the refactoring you can remove the Shims.

Gotcha, so this is more of a WELC-style get unit tests around it at all costs, and then remove once you have refactored to something better. Maybe a follow up article to say, "Now that shims are in place, here's how you get rid of this nastiness" is in order? :)

I agree. Been thinking about how to do that post. I mgiht take that example and run with it. Thanks!!

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.