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

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