次の方法で共有


Evolution of a hand rolled fake

I usually hand roll my own fake objects for my tests. They have always looked a lot like what Stubs generate. I just think that it's so cheap to create them that I don't even need Stubs. In this series I'll assume an interface that looks like this:

   1: interface ITheInterface
  2: {
  3:     void DoSomething(int x);
  4:     int ComputeSomething(int a, int b);
  5: }

When I first started to hand roll my fakes it looked something like this:

   6: private class FakeTheInterface : ITheInterface
  7: {
  8:     public Action<int> DoSomethingHandler { get; set; }
  9:     public Func<int, int, int> ComputeSomethingHandler { get; set; } 
 10:  
 11:     public void DoSomething(int x)
 12:     {
 13:         if (DoSomethingHandler == null)
 14:         {
 15:             Assert.Fail("Unexpected call to DoSomething");
 16:         }
 17:  
 18:         DoSomethingHandler(x);
 19:     }
 20:  
 21:     public int ComputeSomething(int a, int b)
 22:     {
 23:         if (ComputeSomethingHandler == null)
 24:         {
 25:             Assert.Fail("Unexpected call to ComputeSomething");
 26:         }
 27:  
 28:         return ComputeSomethingHandler(a, b);
 29:     }
 30: }

Which gave you a test that looked something like this:

  31: [TestMethod]
 32: public void UsingFake1()
 33: {
 34:     var thing = new FakeTheInterface();
 35:     thing.ComputeSomethingHandler = (a, b) => 42;
 36:     Assert.AreEqual(42, thing.ComputeSomething(0, 0));
 37: }

After a while I realized that I could make the fake a little nicer by doing this:

  38: private class FakeTheInterface : ITheInterface
 39: {
 40:     public Action<int> DoSomethingHandler { get; set; }
 41:     public Func<int, int, int> ComputeSomethingHandler { get; set; }
 42:  
 43:     public void DoSomething(int x)
 44:     {
 45:         Assert.IsNotNull(DoSomethingHandler, 
 46:             "Unexpected call to DoSomething");
 47:         DoSomethingHandler(x);
 48:     }
 49:  
 50:     public int ComputeSomething(int a, int b)
 51:     {
 52:         Assert.IsNotNull(ComputeSomethingHandler, 
 53:             "Unexpected call to ComputeSomething");
 54:         return ComputeSomethingHandler(a, b);
 55:     }
 56: }

But once in a while I came across an interface with a method that had a method like FooHandler. "FooHandlerHandler" is just very confusing. Recently I tried a different approach that looks like this:

  57: private class FakeTheInterface : ITheInterface
 58: {
 59:     private Action<int> doSomething = 
 60:         x => Assert.Fail("Unexpected call to DoSomething({0}).", x);
 61:  
 62:     private Func<int, int, int> computeSomething =
 63:         (a, b) =>
 64:             {
 65:                 Assert.Fail(
 66:                     "Unexpected call to ComputeSomething({0}, {1}).",
 67:                     a, b);
 68:                 return 0;
 69:             };
 70:  
 71:     public FakeTheInterface(
 72:         Action<int> DoSomething = null, 
 73:         Func<int, int, int> ComputeSomething = null)
 74:     {
 75:         doSomething = DoSomething ?? doSomething;
 76:         computeSomething = ComputeSomething ?? computeSomething;
 77:     }
 78:  
 79:     public void DoSomething(int x)
 80:     {
 81:         doSomething(x);
 82:     }
 83:  
 84:     public int ComputeSomething(int a, int b)
 85:     {
 86:         return computeSomething(a, b);
 87:     }
 88: }

Note that I abuse the naming guidelines for arguments in order to make it consistent with the method name. A test using this fake looks like this:

  89: [TestMethod]
 90: public void UsingFake3()
 91: {
 92:     var thing = new FakeTheInterface(
 93:         ComputeSomething: (a, b) => 42);
 94:     Assert.AreEqual(42, thing.ComputeSomething(0, 0));
 95: }

So far I'm happy with this evolution. The only potential problem I see is if I need to replace the implementation half way through a test, but that can still be achieved by having a seperate variable in the test that I use and then change. So all in all it feels like this last evolution will be used (by me) for a while. Suggestions on improvements welcome!

Comments

  • Anonymous
    January 19, 2012
    I prefer it to stub via events. This way I cannot really override the default behaviour of the fake but it does compose very nicely. For return values the event does return always the value of the last subscribed delegate which is exactly what I want.    class FakeITheInterface : ITheInterface    {        public event Action<int> OnDoSomething = x => {};        public void DoSomething(int x)        {            OnDoSomething(x);        }        public event Func<int, int, int> OnComputeSomething = (a, b) => 42;        public int ComputeSomething(int a, int b)        {            return OnComputeSomething(a, b);        }    } To use it you can simply do:            var fake = new FakeITheInterface();            fake.OnDoSomething += x => { };            fake.OnComputeSomething += (a,b) => 45;            int result = fake.ComputeSomething(1,2); This makes it also very clear which methods you do override. Yours,  Alois Kraus

  • Anonymous
    January 19, 2012
    Events is definitly an interesting variant to the property one. And I think OnDoSomething reads better than DoSomethingHandler. But you could have a property called that too I guess... I do not like that you do not fail if an event is not set in a test, but that is easily fixed. Thanks for showing me the event variant.