TDD Tip:方法内部New出来的对象如何Mock

来源:互联网 发布:光翼网络班视频 编辑:程序博客网 时间:2024/05/21 07:55

--内容摘要:解决的问题:方法内部new的对象在测试时希望能够用mock对象去代替。问题:以下方法可以解决,但是很是丑陋,各位大侠是否有更好的方法?描述:如果说把内部的方法放到类的一个公开成员变量,或者放到方法的参数里,我的意见是公开了不应该公开的东西。使用第三方的类库: Mock工具 Rhino.Mocks, IOC: Castle.Windsor1. 现在我又这么一段代码,我想测试Math内部这两个方法&n。。。

转 http://www.pin5i.com/showtopic-21589.html

 

解决的问题:方法内部new的对象在测试时希望能够用mock对象去代替。

问题:以下方法可以解决,但是很是丑陋,各位大侠是否有更好的方法?

描述:如果说把内部的方法放到类的一个公开成员变量,或者放到方法的参数里,我的意见是公开了不应该公开的东西。

使用第三方的类库: Mock工具 Rhino.Mocks, IOC: Castle.Windsor

1. 现在我又这么一段代码,我想测试Math内部这两个方法

  1.     public  class Math

  2.     {

  3.         public long MathAdd(int a, int b)

  4.         {

  5.             Calc c = new Calc();           

  6.             return c.Add(a, b);

  7.         }



  8.         public long MathAdd2Price(int a)

  9.         {

  10.             Calc c = new Calc();

  11.             return c.AddCount(a) + c.count;

  12.         }

  13.     }



  14.     public class Calc

  15.     {

  16.         public int count = 0;

  17.         public long Add(int a, int b)

  18.         {

  19.             return a + b;

  20.         }



  21.         public long AddCount(int num)

  22.         {

  23.             return count + num;

  24.         }

  25.     }
复制代码

2. 我们看到,由于是在内部new的对象,我们就对Calc类产生了很大的依赖,于是我想到注入一个对像,下面是我的设计

  1.   public class ContainerFactory

  2.     {

  3.         public static IWindsorContainer container;

  4.         public bool IsDebug = false;

  5.         private static readonly ContainerFactory instance = new ContainerFactory();



  6.         public static ContainerFactory Instance

  7.         {

  8.             get

  9.             {

  10.                 return instance;

  11.             }

  12.         }



  13.         private ContainerFactory()

  14.         {

  15.             AddAllCompent();

  16.         }



  17.         private void AddAllCompent()

  18.         {

  19.             if (container == null)

  20.             {

  21.                 container = new WindsorContainer();

  22.                 //此处职位演示,未使用接口

  23.                 container.AddComponentWithLifestyle<Calc>("Calc", LifestyleType.Transient);            }

  24.         }

  25. }
复制代码

这样我们的Math类可改成这样

  1.   public  class Math

  2.     {

  3.         public long MathAdd(int a, int b)

  4.         {

  5.           // Calc c = new Calc();

  6.             Calc c =(Calc) ContainerFactory.container["Calc"];

  7.             return c.Add(a, b);

  8.         }



  9.         public long MathAdd2Price(int a)

  10.         {

  11.             Calc c = (Calc)ContainerFactory.container["Calc"];

  12.             return c.AddCount(a) + c.count;

  13.         }



  14.     }
复制代码

3.  但是,我们如何在测试中用我们Mock的对象代替真实的对象呢?下面是我想的一个自己也认为不好的方法,但能凑活着用

  1. public class ContainerFactory

  2.     {

  3.         private IWindsorContainer container;

  4.         public bool IsDebug = false;

  5.         private static readonly ContainerFactory instance = new ContainerFactory();



  6.         public static ContainerFactory Instance

  7.         {

  8.             get

  9.             {

  10.                 return instance;

  11.             }

  12.         }



  13.         private ContainerFactory()

  14.         {

  15.             AddAllCompent();

  16.         }



  17.         private void AddAllCompent()

  18.         {

  19.             if (container == null)

  20.             {

  21.                 container = new WindsorContainer();

  22.                 //此处职位演示,未使用接口

  23.                 container.AddComponentWithLifestyle<Calc>("Calc", LifestyleType.Transient);            }

  24.         }



  25.         #region Calc

  26.         private Calc DebugCalc;

  27.         public Calc    Calc

  28.         {

  29.             get

  30.             {

  31.                 if (IsDebug && DebugCalc != null)

  32.                 {

  33.                     return DebugCalc;

  34.                 }

  35.                 else

  36.                 {

  37.                     return (Calc)container["Calc"];

  38.                 }

  39.             }

  40.             set

  41.             {

  42.                 // just for test, for mock object

  43.                 if (IsDebug)

  44.                 {

  45.                     DebugCalc = value;

  46.                 }

  47.                 else

  48.                 {

  49.                     throw new Exception("just for test");

  50.                 }

  51.             }

  52.         }

  53.         #endregion

  54.     }
复制代码

修改我们的类

  1.   public  class Math

  2.     {

  3.         public long MathAdd(int a, int b)

  4.         {

  5.           // Calc c = new Calc();

  6.             Calc c = ContainerFactory.Instance.Calc;

  7.             return c.Add(a, b);

  8.         }



  9.         public long MathAdd2Price(int a)

  10.         {

  11.             Calc c = ContainerFactory.Instance.Calc;

  12.             return c.AddCount(a) + c.count;

  13.         }



  14.     }
复制代码

这样我们来看我们通过了测试的代码

  1. [TestClass()]

  2.     public class MathTest

  3.     {

  4.         [TestMethod()]

  5.         public void MathAddTest()

  6.         {

  7.             Math m = new Math();



  8.             // 想让真实代码内部,使用的是Mock的对象

  9.             // Arrange

  10.             MockRepository mocks = new MockRepository();

  11.             Calc mockCalc = mocks.Stub<Calc>();

  12.             mockCalc.count = 5;

  13.             ContainerFactory.Instance.IsDebug = true; //这句很重要

  14.             ContainerFactory.Instance.Calc = mockCalc;



  15.             mocks.ReplayAll();

  16.             // Act

  17.             Assert.AreEqual(m.MathAdd(5, 5), 10);

  18.             Assert.AreEqual(mockCalc.AddCount(6), 11);

  19.             mocks.VerifyAll();



  20.             Calc mockCalc2 = mocks.Stub<Calc>();

  21.             mocks.ReplayAll();



  22.             // 这里有问题,我们希望他是7,但实际是12,因为需要测试中的和实际代码用同一个对象,

  23.             // 他保留上次的状态count的值5

  24.             Assert.AreEqual(mockCalc.AddCount(7), 12);

  25.             mocks.VerifyAll();           

  26.         }



  27.         [TestMethod]

  28.     public void MathAddTestActual()

  29.     {

  30.             //这里测试实际使用代码,没用Mock

  31.         Math m = new Math();

  32.         Assert.AreEqual(m.MathAdd(5,6), 11);

  33.         Assert.AreEqual(m.MathAdd2Price(9), 9);

  34.         Calc c = ContainerFactory.Instance.Calc;

  35.         Assert.AreEqual(c.AddCount(5), 5);

  36.         c.count = 20;

  37.         Assert.AreEqual(c.AddCount(5), 25);

  38.         Calc d = ContainerFactory.Instance.Calc;

  39.         Assert.AreEqual(d.AddCount(30), 30);

  40.         Assert.AreEqual(c.count, 20);

  41.         Assert.AreEqual(d.count, 0);

  42.     }

  43.     }
复制代码

总结:这样可以不使用用public的类成员变量,不用通过方法参数注入注入对象


问题:  ContainerFactory代码较多,测试时需要设标志。

原创粉丝点击