解决的问题:方法内部new的对象在测试时希望能够用mock对象去代替。
问题:以下方法可以解决,但是很是丑陋,各位大侠是否有更好的方法?
描述:如果说把内部的方法放到类的一个公开成员变量,或者放到方法的参数里,我的意见是公开了不应该公开的东西。
使用第三方的类库: Mock工具 Rhino.Mocks, IOC: Castle.Windsor
1. 现在我又这么一段代码,我想测试Math内部这两个方法
public class Math
{
public long MathAdd(int a, int b)
{
Calc c = new Calc();
return c.Add(a, b);
}
public long MathAdd2Price(int a)
{
Calc c = new Calc();
return c.AddCount(a) + c.count;
}
}
public class Calc
{
public int count = 0;
public long Add(int a, int b)
{
return a + b;
}
public long AddCount(int num)
{
return count + num;
}
}
2. 我们看到,由于是在内部new的对象,我们就对Calc类产生了很大的依赖,于是我想到注入一个对像,下面是我的设计
public class ContainerFactory
{
public static IWindsorContainer container;
public bool IsDebug = false;
private static readonly ContainerFactory instance = new ContainerFactory();
public static ContainerFactory Instance
{
get
{
return instance;
}
}
private ContainerFactory()
{
AddAllCompent();
}
private void AddAllCompent()
{
if (container == null)
{
container = new WindsorContainer();
//此处职位演示,未使用接口
container.AddComponentWithLifestyle<Calc>("Calc", LifestyleType.Transient); }
}
}
这样我们的Math类可改成这样
public class Math
{
public long MathAdd(int a, int b)
{
// Calc c = new Calc();
Calc c =(Calc) ContainerFactory.container["Calc"];
return c.Add(a, b);
}
public long MathAdd2Price(int a)
{
Calc c = (Calc)ContainerFactory.container["Calc"];
return c.AddCount(a) + c.count;
}
}
3. 但是,我们如何在测试中用我们Mock的对象代替真实的对象呢?下面是我想的一个自己也认为不好的方法,但能凑活着用
public class ContainerFactory
{
private IWindsorContainer container;
public bool IsDebug = false;
private static readonly ContainerFactory instance = new ContainerFactory();
public static ContainerFactory Instance
{
get
{
return instance;
}
}
private ContainerFactory()
{
AddAllCompent();
}
private void AddAllCompent()
{
if (container == null)
{
container = new WindsorContainer();
//此处职位演示,未使用接口
container.AddComponentWithLifestyle<Calc>("Calc", LifestyleType.Transient); }
}
#region Calc
private Calc DebugCalc;
public Calc Calc
{
get
{
if (IsDebug && DebugCalc != null)
{
return DebugCalc;
}
else
{
return (Calc)container["Calc"];
}
}
set
{
// just for test, for mock object
if (IsDebug)
{
DebugCalc = value;
}
else
{
throw new Exception("just for test");
}
}
}
#endregion
}
修改我们的类
public class Math
{
public long MathAdd(int a, int b)
{
// Calc c = new Calc();
Calc c = ContainerFactory.Instance.Calc;
return c.Add(a, b);
}
public long MathAdd2Price(int a)
{
Calc c = ContainerFactory.Instance.Calc;
return c.AddCount(a) + c.count;
}
}
这样我们来看我们通过了测试的代码
[TestClass()]
public class MathTest
{
[TestMethod()]
public void MathAddTest()
{
Math m = new Math();
// 想让真实代码内部,使用的是Mock的对象
// Arrange
MockRepository mocks = new MockRepository();
Calc mockCalc = mocks.Stub<Calc>();
mockCalc.count = 5;
ContainerFactory.Instance.IsDebug = true; //这句很重要
ContainerFactory.Instance.Calc = mockCalc;
mocks.ReplayAll();
// Act
Assert.AreEqual(m.MathAdd(5, 5), 10);
Assert.AreEqual(mockCalc.AddCount(6), 11);
mocks.VerifyAll();
Calc mockCalc2 = mocks.Stub<Calc>();
mocks.ReplayAll();
// 这里有问题,我们希望他是7,但实际是12,因为需要测试中的和实际代码用同一个对象,
// 他保留上次的状态count的值5
Assert.AreEqual(mockCalc.AddCount(7), 12);
mocks.VerifyAll();
}
[TestMethod]
public void MathAddTestActual()
{
//这里测试实际使用代码,没用Mock
Math m = new Math();
Assert.AreEqual(m.MathAdd(5,6), 11);
Assert.AreEqual(m.MathAdd2Price(9), 9);
Calc c = ContainerFactory.Instance.Calc;
Assert.AreEqual(c.AddCount(5), 5);
c.count = 20;
Assert.AreEqual(c.AddCount(5), 25);
Calc d = ContainerFactory.Instance.Calc;
Assert.AreEqual(d.AddCount(30), 30);
Assert.AreEqual(c.count, 20);
Assert.AreEqual(d.count, 0);
}
}
总结:这样可以不使用用public的类成员变量,不用通过方法参数注入注入对象
问题: ContainerFactory代码较多,测试时需要设标志。
其它的问题: 等待高人指出,谢谢!