Skip to content

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Rebuttal to C# Shims
//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
@PProvost

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.

@CoryFoy
Owner

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? :)

@PProvost

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.