Skip to content

Instantly share code, notes, and snippets.

@CoryFoy
Created April 25, 2012 16:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CoryFoy/2491139 to your computer and use it in GitHub Desktop.
Save CoryFoy/2491139 to your computer and use it in GitHub Desktop.
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
Copy link

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
Copy link
Author

CoryFoy commented Apr 25, 2012

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
Copy link

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